home *** CD-ROM | disk | FTP | other *** search
Text File | 2002-07-01 | 86.2 KB | 3,998 lines |
- // Copyright (C) 2001-2002 Raven Software
- //
- // bg_pmove.c -- both games player movement code
- // takes a playerstate and a usercmd as input and returns a modifed playerstate
-
- #include "q_shared.h"
- #include "bg_public.h"
- #include "bg_local.h"
-
- pmove_t *pm;
- pml_t pml;
-
- // Speed scales
- float pm_stopspeed = 100.0f;
- float pm_duckScale = 0.25f;
- float pm_swimScale = 0.50f;
- float pm_wadeScale = 0.70f;
- const float pm_ladderScale = 0.5f;
-
- // Accelerations
- float pm_accelerate = 6.0f;
- float pm_airaccelerate = 1.0f;
- float pm_wateraccelerate = 4.0f;
- float pm_flyaccelerate = 8.0f;
-
- // Frictions
- float pm_headfriction = 0.0f; // Friction when on someones head
- float pm_friction = 6.0f; // Friction when on the ground
- float pm_waterfriction = 3.0f; // Friction when in water
- float pm_ladderfriction = 6.0f; // Friction when on a ladder
- float pm_spectatorfriction = 5.0f; // Friction when flying aroudn as a spectator
-
- int c_pmove = 0;
-
- ladder_t pm_ladders[MAX_LADDERS];
- int pm_laddercount = 0;
-
- static void PM_Weapon_AddInaccuracy ( attackType_t attack );
- static void PM_Weapon_AddKickAngles ( vec3_t kickAngles );
- static void PM_BeginZoomOut ( void );
-
- /*
- ===============
- PM_AddEvent
- ===============
- */
- void PM_AddEvent( int newEvent )
- {
- BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
- }
-
- /*
- ===============
- PM_AddEventWithParm
- ===============
- */
- void PM_AddEventWithParm( int newEvent, int parm )
- {
- BG_AddPredictableEventToPlayerstate( newEvent, parm, pm->ps );
- }
-
- /*
- ===============
- PM_AddTouchEnt
- ===============
- */
- void PM_AddTouchEnt ( int entityNum )
- {
- int i;
-
- // Cant add the world as a touch entity
- if ( entityNum == ENTITYNUM_WORLD )
- {
- return;
- }
-
- // Ensure the max touch limit has not been reached
- if ( pm->numtouch == MAXTOUCH )
- {
- return;
- }
-
- // see if it is already added
- for ( i = 0 ; i < pm->numtouch ; i++ )
- {
- if ( pm->touchents[ i ] == entityNum )
- {
- return;
- }
- }
-
- // add it
- pm->touchents[pm->numtouch] = entityNum;
- pm->numtouch++;
- }
-
-
- /*
- ==================
- PM_ClipVelocity
-
- Slide off of the impacting surface
- ==================
- */
- void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce )
- {
- float backoff;
- float change;
- int i;
-
- backoff = DotProduct (in, normal);
-
- if ( backoff < 0 )
- {
- backoff *= overbounce;
- }
- else
- {
- backoff /= overbounce;
- }
-
- for ( i=0 ; i<3 ; i++ )
- {
- change = normal[i]*backoff;
- out[i] = in[i] - change;
- }
- }
-
-
- /*
- ==================
- PM_Friction
-
- Handles both ground friction and water friction
- ==================
- */
- static void PM_Friction( void )
- {
- vec3_t vec;
- float *vel;
- float speed, newspeed, control;
- float drop;
-
- vel = pm->ps->velocity;
-
- VectorCopy( vel, vec );
- if ( pml.walking )
- {
- vec[2] = 0; // ignore slope movement
- }
-
- speed = VectorLength(vec);
- if (speed < 1)
- {
- vel[0] = 0;
- vel[1] = 0; // allow sinking underwater
- // FIXME: still have z friction underwater?
- return;
- }
-
- drop = 0;
-
- // apply ground friction
- if ( pm->waterlevel <= 1 )
- {
- if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) )
- {
- // if getting knocked back, no friction
- if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) )
- {
- control = speed < pm_stopspeed ? pm_stopspeed : speed;
- drop += control*pm_friction*pml.frametime;
- }
- }
- }
-
- // apply water friction even if just wading
- if ( pm->ps->pm_flags & PMF_LADDER )
- {
- if ( !pml.groundPlane )
- {
- control = speed < pm_stopspeed ? pm_stopspeed : speed;
- drop += control*pm_ladderfriction*pml.frametime;
- }
- }
- else if ( pm->waterlevel > 1 )
- {
- drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
- }
- // If on someones head then use special friction
- else if ( pm->ps->groundEntityNum < MAX_CLIENTS )
- {
- drop = speed*pm_headfriction*pml.frametime;
- }
-
- if ( pm->ps->pm_type == PM_SPECTATOR)
- {
- drop += speed*pm_spectatorfriction*pml.frametime;
- }
-
-
- // scale the velocity
- newspeed = speed - drop;
- if (newspeed < 0)
- {
- newspeed = 0;
- }
- newspeed /= speed;
-
- vel[0] = vel[0] * newspeed;
- vel[1] = vel[1] * newspeed;
- vel[2] = vel[2] * newspeed;
- }
-
-
- /*
- ==============
- PM_Accelerate
-
- Handles user intended acceleration
- ==============
- */
- static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel )
- {
- #if 1
- // q2 style
- int i;
- float addspeed, accelspeed, currentspeed;
-
- currentspeed = DotProduct (pm->ps->velocity, wishdir);
- addspeed = wishspeed - currentspeed;
- if (addspeed <= 0) {
- return;
- }
- accelspeed = accel*pml.frametime*wishspeed;
- if (accelspeed > addspeed) {
- accelspeed = addspeed;
- }
-
- for (i=0 ; i<3 ; i++) {
- pm->ps->velocity[i] += accelspeed*wishdir[i];
- }
- #else
- // proper way (avoids strafe jump maxspeed bug), but feels bad
- vec3_t wishVelocity;
- vec3_t pushDir;
- float pushLen;
- float canPush;
-
- VectorScale( wishdir, wishspeed, wishVelocity );
- VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
- pushLen = VectorNormalize( pushDir );
-
- canPush = accel*pml.frametime*wishspeed;
- if (canPush > pushLen) {
- canPush = pushLen;
- }
-
- VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
- #endif
- }
-
- /*
- ============
- PM_CmdScale
-
- Returns the scale factor to apply to cmd movements
- This allows the clients to use axial -127 to 127 values for all directions
- without getting a sqrt(2) distortion in speed.
- ============
- */
- static float PM_CmdScale( usercmd_t *cmd )
- {
- int max;
- float total;
- float scale;
-
- max = abs( cmd->forwardmove );
- if ( abs( cmd->rightmove ) > max )
- {
- max = abs( cmd->rightmove );
- }
-
- if ( abs( cmd->upmove ) > max )
- {
- max = abs( cmd->upmove );
- }
-
- if ( !max )
- {
- return 0;
- }
-
- total = sqrt( cmd->forwardmove * cmd->forwardmove
- + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
- scale = (float)pm->ps->speed * max / ( 127.0 * total );
-
- return scale;
- }
-
-
- /*
- ================
- PM_SetMovementDir
-
- Determine the rotation of the legs reletive
- to the facing dir
- ================
- */
- static void PM_SetMovementDir( void )
- {
- if ( pm->cmd.forwardmove || pm->cmd.rightmove )
- {
- if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 )
- {
- pm->ps->movementDir = 0;
- }
- else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 )
- {
- pm->ps->movementDir = 1;
- }
- else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 )
- {
- pm->ps->movementDir = 2;
- }
- else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 )
- {
- pm->ps->movementDir = 3;
- }
- else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 )
- {
- pm->ps->movementDir = 4;
- }
- else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 )
- {
- pm->ps->movementDir = 5;
- }
- else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 )
- {
- pm->ps->movementDir = 6;
- }
- else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 )
- {
- pm->ps->movementDir = 7;
- }
- }
- else
- {
- // if they aren't actively going directly sideways,
- // change the animation to the diagonal so they
- // don't stop too crooked
- if ( pm->ps->movementDir == 2 )
- {
- pm->ps->movementDir = 1;
- }
- else if ( pm->ps->movementDir == 6 )
- {
- pm->ps->movementDir = 7;
- }
- }
- }
-
- /*
- =============
- PM_CheckJump
- =============
- */
- static qboolean PM_CheckJump( void )
- {
- if ( pm->ps->pm_time )
- {
- return qfalse;
- }
-
- // Cant jump when ducked
- if ( pm->ps->pm_flags & PMF_DUCKED )
- {
- return qfalse;
- }
-
- // don't allow jump until all buttons are up
- if ( pm->ps->pm_flags & PMF_RESPAWNED )
- {
- return qfalse;
- }
-
- // not holding jump
- if ( pm->cmd.upmove < 10 )
- {
- return qfalse;
- }
-
- // must wait for jump to be released
- if ( pm->ps->pm_debounce & PMD_JUMP )
- {
- // clear upmove so cmdscale doesn't lower running speed
- pm->cmd.upmove = 0;
- return qfalse;
- }
-
- pml.groundPlane = qfalse; // jumping away
- pml.walking = qfalse;
- pm->ps->pm_debounce |= PMD_JUMP;
- pm->ps->pm_flags |= PMF_JUMPING;
-
- pm->ps->groundEntityNum = ENTITYNUM_NONE;
-
- if ( pm->cmd.forwardmove >= 0 )
- {
- PM_ForceLegsAnim( pm->ps, LEGS_JUMP );
- }
- else
- {
- PM_ForceLegsAnim( pm->ps, LEGS_JUMP_BACK );
- pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
- }
-
- // Special case for ladders
- if ( pm->ps->ladder != -1 )
- {
- vec3_t forward;
- VectorCopy ( pm_ladders[pm->ps->ladder].fwd, forward );
- forward[2] = 0;
- VectorNormalize ( forward );
- VectorMA ( pm->ps->velocity, -50, forward, pm->ps->velocity );
- pm->ps->pm_flags |= PMF_LADDER_JUMP;
- return qtrue;
- }
-
- pm->ps->velocity[2] = JUMP_VELOCITY;
-
- return qtrue;
- }
-
- /*
- =============
- PM_CheckWaterJump
- =============
- */
- static qboolean PM_CheckWaterJump( void )
- {
- vec3_t spot;
- int cont;
- vec3_t flatforward;
-
- if (pm->ps->pm_time)
- {
- return qfalse;
- }
-
- // check for water jump
- if ( pm->waterlevel != 2 )
- {
- return qfalse;
- }
-
- flatforward[0] = pml.forward[0];
- flatforward[1] = pml.forward[1];
- flatforward[2] = 0;
- VectorNormalize (flatforward);
-
- VectorMA (pm->ps->origin, 30, flatforward, spot);
- spot[2] += 4;
- cont = pm->pointcontents (spot, pm->ps->clientNum );
- if ( !(cont & CONTENTS_SOLID) )
- {
- return qfalse;
- }
-
- spot[2] += 16;
- cont = pm->pointcontents (spot, pm->ps->clientNum );
- if ( cont )
- {
- return qfalse;
- }
-
- // jump out of water
- VectorScale (pml.forward, 200, pm->ps->velocity);
- pm->ps->velocity[2] = 350;
-
- pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
- pm->ps->pm_time = 2000;
-
- return qtrue;
- }
-
- /*
- ===================
- PM_WaterJumpMove
-
- Flying out of the water
- ===================
- */
- static void PM_WaterJumpMove( void )
- {
- // waterjump has no control, but falls
- PM_StepSlideMove( qtrue );
-
- pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
- if (pm->ps->velocity[2] < 0)
- {
- // cancel as soon as we are falling down again
- pm->ps->pm_flags &= ~PMF_ALL_TIMES;
- pm->ps->pm_time = 0;
- }
- }
-
- /*
- ===================
- PM_WaterMove
- ===================
- */
- static void PM_WaterMove( void )
- {
- int i;
- vec3_t wishvel;
- float wishspeed;
- vec3_t wishdir;
- float scale;
- float vel;
-
- if ( PM_CheckWaterJump() )
- {
- PM_WaterJumpMove();
- return;
- }
-
- #if 0
- // jump = head for surface
- if ( pm->cmd.upmove >= 10 ) {
- if (pm->ps->velocity[2] > -300) {
- if ( pm->watertype == CONTENTS_WATER ) {
- pm->ps->velocity[2] = 100;
- } else {
- pm->ps->velocity[2] = 50;
- }
- }
- }
- #endif
- PM_Friction ();
-
- scale = PM_CmdScale( &pm->cmd );
- //
- // user intentions
- //
- if ( !scale )
- {
- wishvel[0] = 0;
- wishvel[1] = 0;
- wishvel[2] = -60; // sink towards bottom
- }
- else
- {
- for (i=0 ; i<3 ; i++)
- {
- wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
- }
-
- wishvel[2] += scale * pm->cmd.upmove;
- }
-
- VectorCopy (wishvel, wishdir);
- wishspeed = VectorNormalize(wishdir);
-
- if ( wishspeed > pm->ps->speed * pm_swimScale )
- {
- wishspeed = pm->ps->speed * pm_swimScale;
- }
-
- PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
-
- // make sure we can go up slopes easily under water
- if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 )
- {
- vel = VectorLength(pm->ps->velocity);
- // slide along the ground plane
- PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
- pm->ps->velocity, OVERCLIP );
-
- VectorNormalize(pm->ps->velocity);
- VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
- }
-
- PM_SlideMove( qfalse );
- }
-
- /*
- ===================
- PM_FlyMove
- ===================
- */
- static void PM_FlyMove( void )
- {
- int i;
- vec3_t wishvel;
- float wishspeed;
- vec3_t wishdir;
- float scale;
-
- // normal slowdown
- PM_Friction ();
-
- scale = PM_CmdScale( &pm->cmd );
- //
- // user intentions
- //
- if ( !scale )
- {
- wishvel[0] = 0;
- wishvel[1] = 0;
- wishvel[2] = 0;
- }
- else
- {
- for (i=0 ; i<3 ; i++)
- {
- wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
- }
-
- wishvel[2] += scale * pm->cmd.upmove;
- }
-
- VectorCopy (wishvel, wishdir);
- wishspeed = VectorNormalize(wishdir);
-
- PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
-
- PM_StepSlideMove( qfalse );
- }
-
-
- /*
- ===================
- PM_AirMove
-
- ===================
- */
- static void PM_AirMove( void )
- {
- int i;
- vec3_t wishvel;
- float fmove, smove;
- vec3_t wishdir;
- float wishspeed;
- float scale;
- usercmd_t cmd;
-
- PM_Friction();
-
- fmove = pm->cmd.forwardmove;
- smove = pm->cmd.rightmove;
-
- cmd = pm->cmd;
- scale = PM_CmdScale( &cmd );
-
- // set the movementDir so clients can rotate the legs for strafing
- PM_SetMovementDir();
-
- // project moves down to flat plane
- pml.forward[2] = 0;
- pml.right[2] = 0;
- VectorNormalize (pml.forward);
- VectorNormalize (pml.right);
-
- for ( i = 0 ; i < 2 ; i++ ) {
- wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
- }
- wishvel[2] = 0;
-
- VectorCopy (wishvel, wishdir);
- wishspeed = VectorNormalize(wishdir);
- wishspeed *= scale;
-
- // not on ground, so little effect on velocity
- PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);
-
- // we may have a ground plane that is very steep, even
- // though we don't have a groundentity
- // slide along the steep plane
- if ( pml.groundPlane ) {
- PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
- pm->ps->velocity, OVERCLIP );
- }
-
- PM_StepSlideMove ( qtrue );
- }
-
- /*
- ===================
- PM_WalkMove
- ===================
- */
- static void PM_WalkMove( void ) {
- int i;
- vec3_t wishvel;
- float fmove, smove;
- vec3_t wishdir;
- float wishspeed;
- float scale;
- usercmd_t cmd;
- float accelerate;
- float vel;
-
- if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 )
- {
- // begin swimming
- PM_WaterMove();
- return;
- }
-
-
- if ( PM_CheckJump () )
- {
- PM_BeginZoomOut ( );
-
- // jumped away
- if ( pm->waterlevel > 1 )
- {
- PM_WaterMove();
- }
- else
- {
- PM_AirMove();
- }
- return;
- }
-
- PM_Friction ();
-
- fmove = pm->cmd.forwardmove;
- smove = pm->cmd.rightmove;
-
- cmd = pm->cmd;
- scale = PM_CmdScale( &cmd );
-
- // set the movementDir so clients can rotate the legs for strafing
- PM_SetMovementDir();
-
- // project moves down to flat plane
- pml.forward[2] = 0;
- pml.right[2] = 0;
-
- // project the forward and right directions onto the ground plane
- PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
- PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
- //
- VectorNormalize (pml.forward);
- VectorNormalize (pml.right);
-
- for ( i = 0 ; i < 3 ; i++ ) {
- wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
- }
- // when going up or down slopes the wish velocity should Not be zero
- // wishvel[2] = 0;
-
- VectorCopy (wishvel, wishdir);
- wishspeed = VectorNormalize(wishdir);
- wishspeed *= scale;
-
- // clamp the speed lower if ducking
- if ( pm->ps->pm_flags & PMF_DUCKED ) {
- if ( wishspeed > pm->ps->speed * pm_duckScale ) {
- wishspeed = pm->ps->speed * pm_duckScale;
- }
- }
-
- // clamp the speed lower if wading or walking on the bottom
- if ( pm->waterlevel && !(pm->watertype & CONTENTS_LADDER) ) {
- float waterScale;
-
- waterScale = pm->waterlevel / 3.0;
- waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
- if ( wishspeed > pm->ps->speed * waterScale ) {
- wishspeed = pm->ps->speed * waterScale;
- }
- }
-
- // when a player gets hit, they temporarily lose
- // full control, which allows them to be moved a bit
- if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
- {
- accelerate = pm_airaccelerate;
- }
- else
- {
- accelerate = pm_accelerate;
-
- // Accelerate faster when ducked
- if ( pm->ps->pm_flags & PMF_DUCKED )
- {
- accelerate *= 2;
- }
- }
-
- PM_Accelerate (wishdir, wishspeed, accelerate);
-
- if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
- {
- pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
- }
-
- vel = VectorLength(pm->ps->velocity);
-
- // slide along the ground plane
- PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
- pm->ps->velocity, OVERCLIP );
-
- // don't decrease velocity when going up or down a slope
- VectorNormalize(pm->ps->velocity);
- VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
-
- // don't do anything if standing still
- if (!pm->ps->velocity[0] && !pm->ps->velocity[1])
- {
- return;
- }
-
- PM_StepSlideMove( qfalse );
- }
-
- /*
- ===================
- PM_LadderMove
- ===================
- */
- static void PM_LadderMove ( void )
- {
- float wishspeed;
- float scale;
- vec3_t wishdir;
- vec3_t wishvel;
- float accelerate;
-
- if ( PM_CheckJump () )
- {
- return;
- }
-
- PM_Friction ();
-
- scale = PM_CmdScale( &pm->cmd );
-
- accelerate = pm_accelerate;
-
- //
- // user intentions
- //
- if ( !scale )
- {
- wishvel[0] = 0;
- wishvel[1] = 0;
- wishvel[2] = 0;
- }
- else
- {
- int i;
-
- VectorNormalize ( pm_ladders[pm->ps->ladder].fwd );
- VectorNormalize ( pml.forward );
-
- if ( !pml.groundPlane )
- {
- vec3_t mins;
- vec3_t maxs;
- vec3_t offset = {1, 1, 1};
- trace_t tr;
-
- VectorCopy ( pm->mins, mins );
- VectorSubtract ( mins, offset, mins );
- VectorCopy ( pm->maxs, maxs );
- VectorAdd ( maxs, offset, maxs );
-
- pm->trace ( &tr, pm->ps->origin, mins, maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
- if ( tr.fraction == 1.0f || !tr.startsolid )
- {
- if ( pm->cmd.forwardmove >= 0 )
- {
- VectorAdd ( pml.forward, pm_ladders[pm->ps->ladder].fwd, pml.forward );
- }
- else
- {
- VectorSubtract ( pml.forward, pm_ladders[pm->ps->ladder].fwd, pml.forward );
- }
- }
- }
-
- for ( i=0 ; i<3 ; i++ )
- {
- wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove * pm_ladderScale;
- }
-
- // Duck down ladders
- if ( pm->cmd.upmove < 0 )
- {
- wishvel[2] += scale * pm->cmd.upmove;
- }
- }
-
- VectorCopy (wishvel, wishdir);
- wishspeed = VectorNormalize(wishdir);
-
- PM_Accelerate( wishdir, wishspeed, accelerate );
-
- PM_StepSlideMove( qfalse );
- }
-
- /*
- ==============
- PM_DeadMove
- ==============
- */
- static void PM_DeadMove( void ) {
- float forward;
-
- if ( !pml.walking ) {
- return;
- }
-
- // extra friction
-
- forward = VectorLength (pm->ps->velocity);
- forward -= 20;
- if ( forward <= 0 ) {
- VectorClear (pm->ps->velocity);
- } else {
- VectorNormalize (pm->ps->velocity);
- VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
- }
- }
-
-
- /*
- ===============
- PM_NoclipMove
- ===============
- */
- static void PM_NoclipMove( void ) {
- float speed, drop, friction, control, newspeed;
- int i;
- vec3_t wishvel;
- float fmove, smove;
- vec3_t wishdir;
- float wishspeed;
- float scale;
-
- pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
-
- // friction
-
- speed = VectorLength (pm->ps->velocity);
- if (speed < 1)
- {
- VectorCopy (vec3_origin, pm->ps->velocity);
- }
- else
- {
- drop = 0;
-
- friction = pm_friction*1.5; // extra friction
- control = speed < pm_stopspeed ? pm_stopspeed : speed;
- drop += control*friction*pml.frametime;
-
- // scale the velocity
- newspeed = speed - drop;
- if (newspeed < 0)
- newspeed = 0;
- newspeed /= speed;
-
- VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
- }
-
- // accelerate
- scale = PM_CmdScale( &pm->cmd );
-
- scale *= 1.5f;
-
- fmove = pm->cmd.forwardmove;
- smove = pm->cmd.rightmove;
-
- for (i=0 ; i<3 ; i++)
- wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
- wishvel[2] += pm->cmd.upmove;
-
- VectorCopy (wishvel, wishdir);
- wishspeed = VectorNormalize(wishdir);
- wishspeed *= scale;
-
- PM_Accelerate( wishdir, wishspeed, pm_flyaccelerate );
-
- // move
- VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
- }
-
- //============================================================================
-
- /*
- ==============
- PM_Use
-
- Generates a use event
- ==============
- */
- #define USE_DELAY 2000
-
- void PM_Use( void )
- {
- int useTime = 0;
-
- // don't allow attack until all buttons are up
- if ( pm->ps->pm_flags & PMF_RESPAWNED )
- {
- return;
- }
-
- // ignore if not a normal player
- if ( pm->ps->pm_type != PM_NORMAL )
- {
- return;
- }
-
- // check for dead player
- if ( pm->ps->stats[STAT_HEALTH] <= 0 )
- {
- pm->ps->weapon = WP_NONE;
- return;
- }
-
- // Cant use so dont bother letting them try
-
- if ( !(pm->ps->pm_flags & PMF_CAN_USE ) || !(pm->cmd.buttons & BUTTON_USE ) )
- {
- if ( pm->ps->stats[STAT_USEWEAPONDROP] )
- {
- pm->ps->stats[STAT_USEWEAPONDROP] -= pml.msec;
- if ( pm->ps->stats[STAT_USEWEAPONDROP] < 0 )
- {
- pm->ps->stats[STAT_USEWEAPONDROP] = 0;
- }
- }
-
- if ( pm->ps->pm_debounce & PMD_USE )
- {
- pm->ps->pm_debounce &= ~PMD_USE;
- pm->ps->stats[STAT_USETIME] = 0;
- }
- return;
- }
-
- pm->ps->pm_debounce |= PMD_USE;
-
- useTime = pm->ps->stats[STAT_USETIME_MAX];
- if ( useTime )
- {
- int elapsedTime = pm->ps->stats[STAT_USETIME];
-
- if ( elapsedTime < useTime )
- {
- elapsedTime += pml.msec;
- }
-
- pm->ps->stats[STAT_USEWEAPONDROP] += pml.msec;
- if ( pm->ps->stats[STAT_USEWEAPONDROP] > 300 )
- {
- pm->ps->stats[STAT_USEWEAPONDROP] = 300;
- }
-
- if ( elapsedTime >= useTime )
- {
- pm->ps->stats[STAT_USETIME] = 0;
- PM_AddEvent ( EV_USE );
- }
- else
- {
- pm->ps->stats[STAT_USETIME] = elapsedTime;
- }
-
- return;
- }
-
- if ( !(pm->ps->pm_debounce & PMD_USE) )
- {
- PM_AddEvent ( EV_USE );
- }
- }
-
-
- /*
- ================
- PM_FootstepForSurface
-
- Returns an event number apropriate for the groundsurface
- ================
- */
- static void PM_FootstepForSurface( void )
- {
- if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS )
- {
- return;
- }
-
- PM_AddEventWithParm(EV_FOOTSTEP, pml.groundTrace.surfaceFlags & MATERIAL_MASK);
- }
-
-
- /*
- =================
- PM_CrashLand
-
- Check for hard landings that generate sound events
- =================
- */
-
- int minDeltaForDmg = 97;
- int minDeltaForSmallPainSound = 30;
- int minDeltaForBigPainSound = 97;
- int minDeltaForSlowDown = 17;
-
- static void PM_CrashLand( int impactMaterial, vec3_t impactNormal )
- {
- float delta;
- float dist;
- float vel, acc;
- float t;
- float a, b, c, den;
- float f;
- int scaleDelta;
- qboolean jumped;
-
- static vec3_t up = {0,0,1};
-
- // were they juping?
- jumped = (pm->ps->pm_flags&PMF_JUMPING) ? qtrue : qfalse;
-
- pm->ps->pm_flags &= (~PMF_LADDER_JUMP);
- pm->ps->pm_flags &= (~PMF_JUMPING);
-
- // calculate the exact velocity on landing
- dist = pm->ps->origin[2] - pml.previous_origin[2];
- vel = pml.previous_velocity[2];
- acc = -pm->ps->gravity;
-
- a = acc / 2;
- b = vel;
- c = -dist;
-
- den = b * b - 4 * a * c;
- if ( den < 0 )
- {
- return;
- }
-
- t = (-b - sqrt( den ) ) / ( 2 * a );
-
- delta = vel + t * acc;
- delta = delta * delta * 0.000275f;
-
- switch ( pm->waterlevel )
- {
- case 3:
- // never take falling damage if completely underwater
- return;
-
- // reduce falling damage if there is standing water
- case 2:
- delta *= 0.25;
- break;
-
- // reduce falling damage if there is standing water
- case 1:
- delta *= 0.5;
- break;
- }
-
- // Scale the delta based on the normal of the plane we hit
- f = DotProduct ( up, impactNormal );
- if ( f < .25 )
- {
- delta *= f;
- }
-
- // Just hit the ground, no more z velocity or we could bounce
- pm->ps->velocity[2] = 0;
-
- // start footstep cycle over if it wasnt a little jump
- if ( delta > minDeltaForSlowDown )
- {
- pm->ps->bobCycle = 0;
- }
-
- if ( delta < 1 )
- {
- return;
- }
- else if ( jumped && delta >= minDeltaForSlowDown )
- {
- // Cut their forward velocity, this pretty much eliminates strafe jumping
- pm->ps->velocity[0] *= 0.25f;
- pm->ps->velocity[1] *= 0.25f;
-
- pm->ps->pm_time = 500;
- }
-
- // create a local entity event to play the sound
-
- // SURF_NODAMAGE is used for bounce pads where you don't ever
- // want to take damage or play a crunch sound
- scaleDelta = (int)delta;
- if (scaleDelta > 100 + minDeltaForDmg)
- {
- scaleDelta = 100 + minDeltaForDmg;
- }
- scaleDelta -= minDeltaForDmg;
- if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )
- {
- if ( delta > minDeltaForBigPainSound )
- {
- PM_AddEventWithParm(EV_FALL_FAR, scaleDelta | ((impactMaterial & MATERIAL_MASK)<<8));
- }
- else if ( delta > minDeltaForDmg )
- {
- // this is a pain grunt, so don't play it if dead
- if ( pm->ps->stats[STAT_HEALTH] > 0 )
- {
- PM_AddEventWithParm(EV_FALL_MEDIUM, scaleDelta | ((impactMaterial & MATERIAL_MASK)<<8));
- }
- }
- else if ( delta > minDeltaForSlowDown )
- {
- PM_AddEventWithParm(EV_FALL_SHORT, impactMaterial & MATERIAL_MASK );
- }
- else if ( delta > 10 && (pm->cmd.buttons & BUTTON_WALKING) )
- {
- PM_FootstepForSurface();
- }
- }
- }
-
- /*
- =============
- PM_CorrectAllSolid
- =============
- */
- static int PM_CorrectAllSolid( trace_t *trace )
- {
- int i, j, k;
- vec3_t point;
-
- if ( pm->debugLevel )
- {
- Com_Printf("%i:allsolid\n", c_pmove);
- }
-
- // jitter around
- for (i = -1; i <= 1; i++) {
- for (j = -1; j <= 1; j++) {
- for (k = -1; k <= 1; k++) {
- VectorCopy(pm->ps->origin, point);
- point[0] += (float) i;
- point[1] += (float) j;
- point[2] += (float) k;
- pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
- if ( !trace->allsolid ) {
- point[0] = pm->ps->origin[0];
- point[1] = pm->ps->origin[1];
- point[2] = pm->ps->origin[2] - 0.25;
-
- pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
- pml.groundTrace = *trace;
- return qtrue;
- }
- }
- }
- }
-
- pm->ps->groundEntityNum = ENTITYNUM_NONE;
- pml.groundPlane = qfalse;
- pml.walking = qfalse;
-
- return qfalse;
- }
-
-
- /*
- =============
- PM_GroundTraceMissed
-
- The ground trace didn't hit a surface, so we are in freefall
- =============
- */
- static void PM_GroundTraceMissed( void )
- {
- trace_t trace;
- vec3_t point;
-
- if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
- {
- // we just transitioned into freefall
- if ( pm->debugLevel )
- {
- Com_Printf("%i:lift\n", c_pmove);
- }
-
- // if they aren't in a jumping animation and the ground is a ways away, force into it
- // if we didn't do the trace, the player would be backflipping down staircases
- VectorCopy( pm->ps->origin, point );
- point[2] -= 64;
-
- pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
- if ( trace.fraction == 1.0 )
- {
- if ( pm->cmd.forwardmove >= 0 )
- {
- pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
- }
- else
- {
- pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
- }
- }
- }
-
- pm->ps->groundEntityNum = ENTITYNUM_NONE;
- pml.groundPlane = qfalse;
- pml.walking = qfalse;
- }
-
-
- /*
- =============
- PM_GroundTrace
- =============
- */
- static void PM_GroundTrace( void )
- {
- vec3_t point;
- trace_t trace;
- float minWalkNormal;
-
- point[0] = pm->ps->origin[0];
- point[1] = pm->ps->origin[1];
- point[2] = pm->ps->origin[2] - 0.25;
-
- pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
- pml.groundTrace = trace;
-
- // When stuck to antoher player set a flag to let the trigger code know so it can unstick the player
- if ( (trace.allsolid || trace.startsolid) && trace.entityNum < MAX_CLIENTS )
- {
- pm->ps->pm_flags |= PMF_SIAMESETWINS;
- }
-
- // if the trace didn't hit anything, we are in free fall
- if ( trace.fraction == 1.0 ) {
- PM_GroundTraceMissed();
- pml.groundPlane = qfalse;
- pml.walking = qfalse;
- return;
- }
-
- // check if getting thrown off the ground
- if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
- if ( pm->debugLevel ) {
- Com_Printf("%i:kickoff\n", c_pmove);
- }
- // go into jump animation
- if ( pm->cmd.forwardmove >= 0 ) {
- PM_ForceLegsAnim( pm->ps, LEGS_JUMP );
- pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
- } else {
- PM_ForceLegsAnim( pm->ps, LEGS_JUMP_BACK );
- pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
- }
-
- pm->ps->groundEntityNum = ENTITYNUM_NONE;
- pml.groundPlane = qfalse;
- pml.walking = qfalse;
- return;
- }
-
- // slopes that are too steep will not be considered onground
- if ( trace.contents & CONTENTS_TERRAIN )
- {
- minWalkNormal = MIN_WALK_NORMAL_TERRAIN;
- }
- else
- {
- minWalkNormal = MIN_WALK_NORMAL;
- }
-
- if ( trace.plane.normal[2] < minWalkNormal )
- {
- if ( pm->debugLevel )
- {
- Com_Printf("%i:steep\n", c_pmove);
- }
-
- // FIXME: if they can't slide down the slope, let them
- // walk (sharp crevices)
- pm->ps->groundEntityNum = ENTITYNUM_NONE;
- pml.groundPlane = qtrue;
- pml.walking = qfalse;
- return;
- }
-
- pml.groundPlane = qtrue;
- pml.walking = qtrue;
-
- // hitting solid ground will end a waterjump
- if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
- {
- pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
- pm->ps->pm_time = 0;
- }
-
- if ( pm->ps->groundEntityNum == ENTITYNUM_NONE)
- {
- // just hit the ground
- // if ((pml.groundTrace.contents & CONTENTS_TERRAIN) && pml.previous_velocity[2] > -200)
- // {
- // }
- // else
- {
- if ( pm->debugLevel )
- {
- Com_Printf("%i:Land\n", c_pmove);
- }
-
- PM_CrashLand(trace.surfaceFlags & MATERIAL_MASK, trace.plane.normal );
-
- // don't do landing time if we were just going down a slope
- if ( pml.previous_velocity[2] < -200 )
- {
- // don't allow another jump for a little while
- pm->ps->pm_flags |= PMF_TIME_LAND;
- pm->ps->pm_time = 250;
- }
- }
- }
-
- pm->ps->groundEntityNum = trace.entityNum;
-
- // don't reset the z velocity for slopes
- // pm->ps->velocity[2] = 0;
-
- PM_AddTouchEnt( trace.entityNum );
- }
-
-
- /*
- =============
- PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving
- =============
- */
- static void PM_SetWaterLevel( void )
- {
- vec3_t point;
- int cont;
- int sample1;
- int sample2;
-
- //
- // get waterlevel, accounting for ducking
- //
- pm->waterlevel = 0;
- pm->watertype = 0;
-
- point[0] = pm->ps->origin[0];
- point[1] = pm->ps->origin[1];
- point[2] = pm->ps->origin[2] + pm->mins[2];
- cont = pm->pointcontents( point, pm->ps->clientNum );
-
- // See if we are on a ladder too
- if ( !(pm->ps->pm_flags&PMF_LADDER_JUMP) && (cont & CONTENTS_LADDER) )
- {
- if ( pm->ps->ladder == -1 )
- {
- pm->ps->ladder = BG_FindLadder ( pm->ps->origin );
- }
-
- pm->ps->pm_flags |= PMF_LADDER;
- }
- else
- {
- pm->ps->ladder = -1;
- pm->ps->pm_flags &= ~PMF_LADDER;
- }
-
- if ( cont & MASK_WATER )
- {
- sample2 = pm->ps->viewheight - MINS_Z;
- sample1 = sample2 / 2;
-
- pm->watertype = cont;
- pm->waterlevel = 1;
- point[2] = pm->ps->origin[2] + MINS_Z + sample1;
- cont = pm->pointcontents (point, pm->ps->clientNum );
- if ( cont & MASK_WATER )
- {
- pm->waterlevel = 2;
- point[2] = pm->ps->origin[2] + MINS_Z + sample2;
- cont = pm->pointcontents (point, pm->ps->clientNum );
- if ( cont & MASK_WATER )
- {
- pm->waterlevel = 3;
- }
- }
- }
- }
-
- /*
- ==============
- PM_CheckCrouchJump
-
- Handles crouch jumping
- ==============
- */
- static void PM_CheckCrouchJump ( void )
- {
- // Already crouch jumping so check to see if its over
- if ( pm->ps->pm_flags & PMF_CROUCH_JUMP )
- {
- // If they are on the ground the crouch jump is over
- if ( pml.groundPlane )
- {
- pm->ps->pm_flags &= ~PMF_CROUCH_JUMP;
- }
- }
- else
- {
- // If not on the ground and still heading up then crouch jump is possible.
- if ( !pml.groundPlane && (pm->ps->pm_flags & PMF_JUMPING) && pm->cmd.upmove < 0 )
- {
- pm->ps->pm_flags |= PMF_CROUCH_JUMP;
- }
- }
-
- // Check again if still crouch jumping and if so alter the view height
- // so the client doesnt look like they are ducking in mid air
- if ( (pm->ps->pm_flags & PMF_CROUCH_JUMP) && (pm->ps->pm_flags & PMF_JUMPING) )
- {
- // If still ducked look for windows
- if ( pm->ps->pm_flags & PMF_DUCKED )
- {
- trace_t trace;
- vec3_t maxs;
-
- VectorCopy ( pm->maxs, maxs );
- maxs[2] = DEFAULT_PLAYER_Z_MAX;
-
- pm->trace (&trace, pm->ps->origin, pm->mins, maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
- if ( !(trace.allsolid || trace.startsolid) )
- {
- pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
- }
- }
- }
- }
-
- /*
- ==============
- PM_CheckDuck
-
- Sets mins, maxs, and pm->ps->viewheight
- ==============
- */
- static void PM_CheckDuck (void)
- {
- trace_t trace;
-
- if ( (pm == 0) || (pm->ps == 0) )
- {
- return;
- }
-
- pm->mins[0] = -15;
- pm->mins[1] = -15;
-
- pm->maxs[0] = 15;
- pm->maxs[1] = 15;
-
- pm->mins[2] = MINS_Z;
-
- if (pm->ps->pm_type == PM_DEAD)
- {
- pm->maxs[2] = DEAD_PLAYER_Z_MAX;
- pm->ps->viewheight = DEAD_VIEWHEIGHT;
- return;
- }
-
- // duck or prone
- if (pm->cmd.upmove < 0)
- {
- // assume ducked at first
- pm->ps->pm_flags |= PMF_DUCKED;
- }
- else
- { // stand up if possible
- if ( pm->ps->pm_flags & PMF_DUCKED )
- {
- pm->maxs[2] = DEFAULT_PLAYER_Z_MAX;
- pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
- if (!trace.allsolid)
- {
- pm->ps->pm_flags &= ~PMF_DUCKED;
- }
- }
- }
-
- if ( (pm->ps->pm_flags & PMF_DUCKED) )
- {
- pm->maxs[2] = CROUCH_PLAYER_Z_MAX;
- pm->ps->viewheight = CROUCH_VIEWHEIGHT;
- }
- else
- {
- pm->maxs[2] = DEFAULT_PLAYER_Z_MAX;
- pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
- }
- }
-
- /*
- ===============
- PM_Footsteps
- ===============
- */
- static void PM_Footsteps( void )
- {
- float bobmove;
- int old;
- qboolean footstep;
-
- //
- // calculate speed and cycle to be used for
- // all cyclic walking effects
- //
- pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
- + pm->ps->velocity[1] * pm->ps->velocity[1] );
-
- if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
- {
-
- if ( pm->ps->pm_flags & PMF_LADDER )
- {
- }
- else
- {
- // airborne leaves position in cycle intact, but doesn't advance
- if ( pm->waterlevel > 1 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_SWIM );
- }
- return;
- }
- }
-
- // if not trying to move
- if ( !pm->cmd.forwardmove && !pm->cmd.rightmove )
- {
- if ( pm->xyspeed < 5 )
- {
- pm->ps->bobCycle = 0; // start at beginning of cycle again
-
- if ( pm->ps->pm_flags & PMF_DUCKED )
- {
- if ( pm->ps->leanTime - LEAN_TIME < 0 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEAN_CROUCH_LEFT );
- }
- else if ( pm->ps->leanTime - LEAN_TIME > 0 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEAN_CROUCH_RIGHT );
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_IDLE_CROUCH );
- }
- }
- else
- {
- if ( pm->ps->leanTime - LEAN_TIME < 0 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEAN_LEFT );
- }
- else if ( pm->ps->leanTime - LEAN_TIME > 0 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEAN_RIGHT );
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, TORSO_IDLE_PISTOL );
- }
- }
- }
- return;
- }
-
-
- footstep = qfalse;
-
- if ( (pm->ps->pm_flags & PMF_DUCKED) && (pm->ps->groundEntityNum != ENTITYNUM_NONE ) )
- {
- bobmove = 0.25; // ducked characters bob much faster
-
- if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_WALK_CROUCH_BACK );
- }
- else
- {
- if ( pm->ps->leanTime - LEAN_TIME < 0 )
- {
- if ( pm->cmd.rightmove > 0 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEANLEFT_CROUCH_WALKRIGHT );
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEANLEFT_CROUCH_WALKLEFT );
- }
- }
- else if ( pm->ps->leanTime - LEAN_TIME > 0 )
- {
- if ( pm->cmd.rightmove > 0 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEANRIGHT_CROUCH_WALKRIGHT );
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEANRIGHT_CROUCH_WALKLEFT );
- }
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_WALK_CROUCH );
- }
- }
- }
- else
- {
- if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
- {
- PM_BeginZoomOut ( );
-
- bobmove = 0.4f; // faster speeds bob faster
- if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_RUN_BACK );
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_RUN );
- }
- footstep = qtrue;
- }
- else
- {
- bobmove = 0.3f; // walking bobs slow
- if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_WALK_BACK );
- }
- else
- {
- if ( pm->ps->leanTime - LEAN_TIME < 0 )
- {
- if ( pm->cmd.rightmove > 0 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEANLEFT_WALKRIGHT );
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEANLEFT_WALKLEFT );
- }
- }
- else if ( pm->ps->leanTime - LEAN_TIME > 0 )
- {
- if ( pm->cmd.rightmove > 0 )
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEANRIGHT_WALKRIGHT );
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_LEANRIGHT_WALKLEFT );
- }
- }
- else
- {
- PM_ContinueLegsAnim( pm->ps, LEGS_WALK );
- }
- }
- }
- }
-
- // check for footstep / splash sounds
- old = pm->ps->bobCycle;
- pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
-
- // if we just crossed a cycle boundary, play an apropriate footstep event
- if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
- {
- if ( pm->waterlevel == 0 )
- {
- // on ground will only play sounds if running
- if ( footstep && !pm->noFootsteps )
- {
- PM_FootstepForSurface();
- }
- }
- else if ( pm->waterlevel == 1 )
- {
- // splashing
- PM_AddEvent( EV_WATER_FOOTSTEP );
- }
- else if ( pm->waterlevel == 2 )
- {
- // wading / swimming at surface
- PM_AddEvent( EV_SWIM );
- }
- else if ( pm->waterlevel == 3 )
- {
- // no sound when completely underwater
- }
- }
- }
-
- /*
- ==============
- PM_WaterEvents
-
- Generate sound events for entering and leaving water
- ==============
- */
- static void PM_WaterEvents( void )
- {
- if ( !pml.previous_waterlevel && pm->waterlevel == 1 )
- {
- PM_AddEvent( EV_WATER_TOUCH );
- }
- else if ( pml.previous_waterlevel <= 1 && pm->waterlevel > 1 )
- {
- if ( pm->ps->velocity[2] < -100 )
- {
- PM_AddEvent( EV_WATER_LAND );
- }
- }
-
- //
- // check for head just coming out of water
- //
- if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
- PM_AddEvent( EV_WATER_CLEAR );
- }
- }
-
- /*
- ===============
- PM_SetWeaponTime
- ===============
- */
- static void PM_SetWeaponTime ( TAnimWeapon *aW )
- {
- TAnimInfoWeapon *aIW;
-
- if(!aW)
- {
- assert(0);
- }
-
- // Weapon model info tells us how long the anim is
- aIW = aW->mWeaponModelInfo;
- if ( !aIW )
- {
- return;
- }
-
- pm->ps->weaponTime = 1000.0f / aIW->mFPS[0] * aIW->mNumFrames[0] / aIW->mSpeed;
- pm->ps->weaponAnimTime = pm->ps->weaponTime;
- }
-
- /*
- ===============
- BG_GetWeaponNote
- ===============
- */
- TNoteTrack *BG_GetWeaponNote( playerState_t* ps, int weapon, int anim, int animChoice, int callbackStep )
- {
- TAnimWeapon *aW;
- TAnimInfoWeapon *aIW;
- TNoteTrack *note;
- int n=0;
-
- note = NULL;
- aW=BG_GetInviewAnimFromIndex( weapon, anim&~ANIM_TOGGLEBIT);
- if (!aW)
- {
- return 0;
- }
-
- aIW = aW->mWeaponModelInfo;
- if ( !aIW )
- {
- return 0;
- }
-
- // Find the callback for the given step
- for ( note = aIW->mNoteTracks[ps->weaponAnimIdChoice], n=0; note && n < callbackStep; note = note->mNext, n++ )
- {
- // Do nothing, loop does it all
- }
-
- return(note);
- }
-
- /*
- ==============
- PM_CheckWeaponNotes
- ==============
- */
- void PM_CheckWeaponNotes ( void )
- {
- playerState_t *ps;
- TAnimWeapon *aW;
- TAnimInfoWeapon *aIW;
- TNoteTrack *note;
- int step;
- int stepTime;
-
- pm->ps->weaponCallbackTime += pml.msec;
-
- // 1st note step.
- step = 0;
- ps = pm->ps;
- aW = BG_GetInviewAnimFromIndex ( ps->weapon, (ps->weaponAnimId&~ANIM_TOGGLEBIT) );
-
- assert ( aW );
- if ( !aW )
- {
- return;
- }
-
- // Get the cached weapon model info
- aIW = aW->mWeaponModelInfo;
- if ( !aIW )
- {
- return;
- }
-
- stepTime = 1000.0f / aIW->mFPS[pm->ps->weaponAnimIdChoice] / aIW->mSpeed;;
- note = aIW->mNoteTracks[pm->ps->weaponAnimIdChoice];
-
- while(note)
- {
- if( pm->ps->weaponCallbackTime >= note->mFrame * stepTime )
- {
- if(step > pm->ps->weaponCallbackStep)
- {
- if( !Q_stricmp("fire",note->mNote) || !Q_stricmp("altfire",note->mNote) )
- {
- if(pm->ps->weaponstate==WEAPON_FIRING)
- {
- int seed;
-
- // Update the seed
- seed = pm->ps->stats[STAT_SEED];
- Q_rand ( &seed );
- seed = seed & 0xFFFF;
- pm->ps->stats[STAT_SEED] = seed;
-
- PM_AddEvent(EV_FIRE_WEAPON);
- PM_Weapon_AddInaccuracy(ATTACK_NORMAL);
- PM_Weapon_AddKickAngles(weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].maxKickAngles);
-
-
- }
- else if(pm->ps->weaponstate==WEAPON_FIRING_ALT)
- {
- PM_AddEvent(EV_ALT_FIRE);
- PM_Weapon_AddKickAngles(weaponData[pm->ps->weapon].attack[ATTACK_ALTERNATE].maxKickAngles);
- }
- }
-
- PM_AddEventWithParm ( EV_WEAPON_CALLBACK,
- ((step&0xFF) << 24) +
- ((pm->ps->weaponAnimIdChoice&0xFF)<<16) +
- (((ps->weaponAnimId&~ANIM_TOGGLEBIT)&0xFF)<<8) +
- pm->ps->weapon);
-
- pm->ps->weaponCallbackStep=step;
- }
- }
-
- step++;
- note=note->mNext;
- }
- }
-
- /*
- ===============
- PM_SetWeaponAnimChoice
- ===============
- */
- void PM_SetWeaponAnimChoice(TAnimWeapon *aW)
- {
- TAnimInfoWeapon *aIW;
-
- if(!aW)
- {
- assert(0);
- return;
- }
-
- // Get the cached weapon model info
- aIW = aW->mWeaponModelInfo;
- if ( !aIW )
- {
- return;
- }
-
- pm->ps->weaponAnimIdChoice = rand()%aIW->mNumChoices;
- }
-
- /*
- ===============
- PM_GetAnimFromName
- ===============
- */
- TAnimWeapon* PM_GetAnimFromName ( char *animName, playerState_t *ps, int *animIndex )
- {
- TAnimWeapon *aW=0;
- TAnimInfoWeapon *aIW=0;
- char tempname[MAX_QPATH];
-
- switch(ps->weapon)
- {
- case WP_KNIFE:
- if(!strcmp(animName,"charge"))
- {
- // Get 'prefire' anim.
- aW=BG_GetInviewAnim(pm->ps->weapon,"prefire",animIndex);
- PM_SetWeaponAnimChoice(aW);
- }
- else if(!strcmp(animName,"fire"))
- {
- aW=BG_GetInviewAnimFromIndex(ps->weapon,ps->weaponAnimId&~ANIM_TOGGLEBIT);
- if((!strcmp(aW->mName,"prefire"))||strstr(aW->mName,"firetrans"))
- {
- // Get 'fire' anim.
- aW=BG_GetInviewAnim(ps->weapon,"fire",animIndex);
- PM_SetWeaponAnimChoice(aW);
- }
- else if(!strcmp(aW->mName,"fire"))
- {
- // Get 'firetrans' anim. We don't call PM_SetWeaponAnimChoice()
- // because the firetrans anims are matched to the fire anims.
- aIW=BG_GetInviewModelAnim(ps->weapon,"weaponmodel","fire");
- strcpy(tempname,aIW->mTransition[ps->weaponAnimIdChoice]);
- aW=BG_GetInviewAnim(ps->weapon,tempname,animIndex);
- }
- else
- {
- // Get 'prefire' anim.
- aW=BG_GetInviewAnim(pm->ps->weapon,"fire",animIndex);
- PM_SetWeaponAnimChoice(aW);
- }
- }
- else if(!strcmp(animName,"fireend"))
- {
- aW=BG_GetInviewAnim(ps->weapon,"fireend1",animIndex);
- PM_SetWeaponAnimChoice(aW);
- }
- else
- {
- // Nothing clever about the other sequences.
- aW=BG_GetInviewAnim(pm->ps->weapon,animName,animIndex);
- PM_SetWeaponAnimChoice(aW);
- }
- break;
-
- case WP_MM1_GRENADE_LAUNCHER:
- case WP_M590_SHOTGUN:
- if(!strcmp(animName,"reload"))
- {
- aW=BG_GetInviewAnimFromIndex(ps->weapon,ps->weaponAnimId&~ANIM_TOGGLEBIT);
- if(!strcmp(aW->mName,"reloadbegin")||!strcmp(aW->mName,"reloadshell"))
- {
- // Get 'reloadshell' anim.
- aW=BG_GetInviewAnim(ps->weapon,"reloadshell",animIndex);
- }
- else
- {
- // Get 'reloadbegin' anim.
- aW=BG_GetInviewAnim(pm->ps->weapon,"reloadbegin",animIndex);
- }
- }
- else if(!strcmp(animName,"reloadend"))
- {
- // Get 'reloadend' anim.
- aW=BG_GetInviewAnim(ps->weapon,"reloadend",animIndex);
- }
- else
- {
- // Nothing clever about the other sequences.
- aW=BG_GetInviewAnim(pm->ps->weapon,animName,animIndex);
- }
- PM_SetWeaponAnimChoice(aW);
- break;
-
- case WP_M84_GRENADE:
- case WP_SMOHG92_GRENADE:
- case WP_ANM14_GRENADE:
- case WP_M15_GRENADE:
- if(!strcmp(animName,"charge"))
- {
- // Get 'throwbegin' anim.
- aW=BG_GetInviewAnim(ps->weapon,"throwbegin",animIndex);
- }
- else if(!strcmp(animName,"altcharge"))
- {
- // Get 'throwbegin' anim.
- aW=BG_GetInviewAnim(ps->weapon,"altthrowbegin",animIndex);
- }
- else if(!strcmp(animName,"fire"))
- {
- // Get 'throwend' anim.
- aW=BG_GetInviewAnim(ps->weapon,"throwend",animIndex);
- }
- else if(!strcmp(animName,"altfire"))
- {
- // Get 'throwend' anim.
- aW=BG_GetInviewAnim(ps->weapon,"altthrowend",animIndex);
- }
- else
- {
- // Nothing clever about the other sequences.
- aW=BG_GetInviewAnim(pm->ps->weapon,animName,animIndex);
- }
- PM_SetWeaponAnimChoice(aW);
- break;
-
- default:
- // Other weapons don't do anything fancy.
- aW=BG_GetInviewAnim(ps->weapon,animName,animIndex);
- PM_SetWeaponAnimChoice(aW);
- break;
- }
-
- return(aW);
- }
-
- /*
- ===============
- BG_GetWeaponAnim
- ===============
- */
- TAnimWeapon *BG_GetWeaponAnim(int weaponAction,playerState_t *ps,int *animIndex)
- {
- TAnimWeapon *aW=0;
-
- switch(weaponAction)
- {
- case WACT_READY:
- aW=PM_GetAnimFromName("ready",ps,animIndex);
- break;
- case WACT_IDLE:
- aW=PM_GetAnimFromName("idle",ps,animIndex);
- break;
- case WACT_FIRE:
- aW=PM_GetAnimFromName("fire",ps,animIndex);
- break;
- case WACT_FIRE_END:
- aW=PM_GetAnimFromName("fireend",ps,animIndex);
- break;
- case WACT_ALTFIRE:
- aW=PM_GetAnimFromName("altfire",ps,animIndex);
- break;
- case WACT_ALTFIRE_END:
- aW=PM_GetAnimFromName("altfireend",ps,animIndex);
- break;
- case WACT_RELOAD:
- aW=PM_GetAnimFromName("reload",ps,animIndex);
- break;
- case WACT_ALTRELOAD:
- aW=PM_GetAnimFromName("altreload",ps,animIndex);
- break;
- case WACT_RELOAD_END:
- aW=PM_GetAnimFromName("reloadend",ps,animIndex);
- break;
- case WACT_PUTAWAY:
- aW=PM_GetAnimFromName("done",ps,animIndex);
- break;
- case WACT_ZOOMIN:
- aW=PM_GetAnimFromName("zoomin",ps,animIndex);
- break;
- case WACT_ZOOMOUT:
- aW=PM_GetAnimFromName("zoomout",ps,animIndex);
- break;
- case WACT_CHARGE:
- aW=PM_GetAnimFromName("charge",ps,animIndex);
- break;
- case WACT_ALTCHARGE:
- aW=PM_GetAnimFromName("altcharge",ps,animIndex);
- break;
- default:
- Com_Printf("Anim unknown: %i\n",weaponAction);
- break;
- }
-
- return(aW);
- }
-
- /*
- ===============
- PM_HandleWeaponAction
- ===============
- */
- static void PM_HandleWeaponAction(int weaponAction)
- {
- TAnimWeapon *aW;
- int animIndex;
-
- aW = BG_GetWeaponAnim ( weaponAction, pm->ps, &animIndex );
- if(!aW)
- {
- return;
- }
-
- // Reset callback timer. We have to account for any remaining weapontime
- // or we could miss off callbacks during sequences that are short in duration.
- pm->ps->weaponCallbackTime = -pm->ps->weaponTime;
- pm->ps->weaponCallbackStep = -1;
-
- PM_SetWeaponTime(aW);
- if((pm->ps->weaponAnimId&~ANIM_TOGGLEBIT)==animIndex)
- {
- animIndex=pm->ps->weaponAnimId^ANIM_TOGGLEBIT;
- }
-
- pm->ps->weaponAnimId = animIndex;
- }
-
- /*
- ===============
- PM_BeginZoomIn
- ===============
- */
- static void PM_BeginZoomIn(void)
- {
- // Reset the zom fov if not rezooming
- if ( !(pm->ps->pm_flags & PMF_ZOOM_REZOOM) )
- {
- pm->ps->zoomFov = 0;
- }
-
- pm->ps->weaponstate=WEAPON_ZOOMIN;
- PM_HandleWeaponAction(WACT_ZOOMIN);
- }
-
- /*
- ===============
- PM_BeginZoomOut
- ===============
- */
- static void PM_BeginZoomOut(void)
- {
- if ( !(pm->ps->pm_flags & PMF_ZOOMED) )
- {
- return;
- }
-
- if ( !(pm->ps->pm_flags & PMF_ZOOM_DEFER_RELOAD ) )
- {
- pm->ps->zoomFov = 0;
- }
-
- pm->ps->weaponstate=WEAPON_ZOOMOUT;
- PM_HandleWeaponAction(WACT_ZOOMOUT);
- pm->ps->zoomTime=pm->ps->commandTime;
- pm->ps->pm_flags &= ~(PMF_ZOOM_LOCKED|PMF_ZOOM_REZOOM|PMF_ZOOMED);
- }
-
-
- /*
- ===============
- PM_BeginWeaponChange
- ===============
- */
- static void PM_BeginWeaponChange(int weapon)
- {
- if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS )
- {
- return;
- }
-
- if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) )
- {
- return;
- }
-
- if ( pm->ps->weaponstate == WEAPON_DROPPING )
- {
- return;
- }
-
- // Dont allow switching to the weapon the client is already using
- if ( pm->ps->weapon == weapon )
- {
- PM_AddEvent(EV_CHANGE_WEAPON_CANCELLED );
-
- if ( pm->ps->weaponTime <= 0 )
- {
- // Add a little delay so it doenst fire because this was caused by
- // the menu selection
- pm->ps->weaponTime = 150;
- }
-
- return;
- }
-
- // turn off any kind of zooming when weapon switching.
- pm->ps->zoomFov = 0;
- pm->ps->zoomTime = 0;
- pm->ps->pm_flags &= ~(PMF_ZOOM_FLAGS);
-
- // Clear the weapon time
- pm->ps->weaponTime = 0;
- pm->ps->weaponFireBurstCount = 0;
- pm->ps->weaponAnimTime = 0;
-
- PM_AddEvent(EV_CHANGE_WEAPON);
- pm->ps->weaponstate = WEAPON_DROPPING;
-
- if( pm->ps->weapon >= WP_M84_GRENADE && pm->ps->weapon <= WP_M15_GRENADE && pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon] <= 0 )
- {
- // We don't want to play the 'putaway' anim for the grenades if we are out of grenades!
- return;
- }
-
- PM_HandleWeaponAction(WACT_PUTAWAY);
-
- PM_StartTorsoAnim( pm->ps, weaponData[pm->ps->weapon].animDrop, pm->ps->weaponAnimTime );
- }
-
- /*
- ===============
- PM_FinishWeaponChange
- ===============
- */
- static void PM_FinishWeaponChange( void )
- {
- int weapon;
-
- weapon = pm->cmd.weapon & ~WP_DELAYED_CHANGE_BIT;
-
- if( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS )
- {
- weapon = WP_KNIFE;
- }
-
- if(!( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) )
- {
- weapon = WP_KNIFE;
- }
-
- PM_AddEvent(EV_READY_WEAPON);
-
- pm->ps->weapon = weapon;
- pm->ps->weaponstate = WEAPON_RAISING;
- pm->ps->weaponTime = 0;
- pm->ps->weaponAnimTime = 0;
-
- // Default to auto (or next available fire mode).
- if ( pm->ps->firemode[pm->ps->weapon] == WP_FIREMODE_NONE )
- {
- pm->ps->firemode[pm->ps->weapon] = BG_FindFireMode ( pm->ps->weapon, ATTACK_NORMAL, WP_FIREMODE_AUTO );
- }
-
- // We don't want to play the 'takeout' anim for the grenades if we are about to reload anyway
- if( pm->ps->weapon >= WP_M84_GRENADE && pm->ps->weapon <= WP_M15_GRENADE && pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon] <=0 )
- {
- return;
- }
-
- PM_HandleWeaponAction(WACT_READY);
-
- pm->ps->weaponTime = min(500,pm->ps->weaponTime);
-
- PM_StartTorsoAnim( pm->ps, weaponData[pm->ps->weapon].animRaise, pm->ps->weaponAnimTime );
- }
-
- /*
- ==============
- PM_LoadShell
-
- Only used for the M590 shotgun.
- ==============
- */
- void PM_LoadShell(void)
- {
- pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon]++;
- pm->ps->ammo[weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].ammoIndex]--;
- }
-
- /*
- ==============
- PM_StartRefillClip
- ==============
- */
- void PM_StartRefillClip ( attackType_t attack )
- {
- int extra;
-
- assert ( attack >= ATTACK_NORMAL && attack < ATTACK_MAX );
-
- // Sniper rifle should unzoom first before reloading.
- if( pm->ps->pm_flags & PMF_ZOOMED )
- {
- pm->ps->pm_flags |= PMF_ZOOM_DEFER_RELOAD;
- PM_BeginZoomOut();
- return;
- }
-
- pm->ps->weaponstate=(attack==ATTACK_ALTERNATE)?WEAPON_RELOADING_ALT:WEAPON_RELOADING;
- pm->ps->weaponFireBurstCount=0;
-
- if(pm->ps->weapon!=WP_KNIFE)
- {
- // We don't want to play the reload anim for the knife, as it is part of
- // the throw anim anyway.
- if(attack==ATTACK_ALTERNATE)
- {
- PM_HandleWeaponAction(WACT_ALTRELOAD);
- }
- else
- {
- PM_HandleWeaponAction(WACT_RELOAD);
- }
- }
-
- if( pm->ps->weapon==WP_M590_SHOTGUN || pm->ps->weapon == WP_MM1_GRENADE_LAUNCHER )
- {
- PM_StartTorsoAnim ( pm->ps, weaponData[pm->ps->weapon].animReloadStart, pm->ps->weaponTime );
- return;
- }
-
- extra = weaponData[pm->ps->weapon].attack[attack].clipSize;
- extra -= pm->ps->clip[attack][pm->ps->weapon];
-
- if(pm->ps->ammo[weaponData[pm->ps->weapon].attack[attack].ammoIndex]<extra)
- {
- extra=pm->ps->ammo[weaponData[pm->ps->weapon].attack[attack].ammoIndex];
- }
-
- pm->ps->clip[attack][pm->ps->weapon]+=extra;
- pm->ps->ammo[weaponData[pm->ps->weapon].attack[attack].ammoIndex]-=extra;
-
- // Rezoom the sniper rifle, if it was zoomed before we started reloading.
- if(pm->ps->pm_flags & PMF_ZOOM_DEFER_RELOAD )
- {
- pm->ps->pm_flags |= PMF_ZOOM_REZOOM;
- pm->ps->pm_flags &= ~PMF_ZOOM_DEFER_RELOAD;
- }
-
- PM_StartTorsoAnim ( pm->ps, weaponData[pm->ps->weapon].animReload, pm->ps->weaponTime );
- }
-
- /*
- ==============
- PM_EndRefillClip
- ==============
- */
- void PM_EndRefillClip(void)
- {
- pm->ps->weaponstate=WEAPON_READY;
- pm->ps->weaponFireBurstCount = 0;
- }
-
- /*
- ===============
- PM_GetAttackButtons
- ===============
- */
- int PM_GetAttackButtons(void)
- {
- int buttons=pm->cmd.buttons;
-
- // Debounce firemode select button.
- if ( buttons & BUTTON_FIREMODE )
- {
- if(!(pm->ps->pm_debounce & PMD_FIREMODE))
- {
- pm->ps->pm_debounce |= PMD_FIREMODE;
- }
- else
- {
- buttons &= ~BUTTON_FIREMODE;
- }
- }
- else
- {
- pm->ps->pm_debounce &= ~PMD_FIREMODE;
- }
-
- // Handle firebutton in varous firemodes.
- switch( pm->ps->firemode[pm->ps->weapon] )
- {
- case WP_FIREMODE_AUTO:
- break;
-
- case WP_FIREMODE_BURST:
- // Debounce attack button and disable other buttons during burst fire.
- if(buttons&BUTTON_ATTACK)
- {
- if( !(pm->ps->pm_debounce & PMD_ATTACK))
- {
- pm->ps->pm_debounce |= PMD_ATTACK;
- if(!pm->ps->weaponFireBurstCount)
- {
- pm->ps->weaponFireBurstCount=3;
- }
- }
- else
- {
- buttons &= ~BUTTON_ATTACK;
- }
- }
- else
- {
- pm->ps->pm_debounce &= ~PMD_ATTACK;
- }
- if(pm->ps->weaponFireBurstCount)
- {
- buttons|=BUTTON_ATTACK;
- buttons&=~BUTTON_ALT_ATTACK;
- buttons&=~BUTTON_RELOAD;
- buttons&=~BUTTON_ZOOMIN;
- buttons&=~BUTTON_ZOOMOUT;
- buttons&=~BUTTON_FIREMODE;
- }
- break;
-
- case WP_FIREMODE_SINGLE:
- // Debounce attack button.
- if(buttons&BUTTON_ATTACK)
- {
- if(!(pm->ps->pm_debounce & PMD_ATTACK))
- {
- pm->ps->pm_debounce |= PMD_ATTACK;
- }
- else
- {
- buttons&=~BUTTON_ATTACK;
- }
- }
- else
- {
- pm->ps->pm_debounce &= ~PMD_ATTACK;
- }
-
- break;
- }
-
- // Handle single fire alt fire attacks or the sniper zoom
- if ( pm->ps->weapon == WP_MSG90A1 || (weaponData[pm->ps->weapon].attack[ATTACK_ALTERNATE].weaponFlags & (1<<WP_FIREMODE_SINGLE)) )
- {
- if ( buttons & BUTTON_ALT_ATTACK )
- {
- if( !(pm->ps->pm_debounce & PMD_ALTATTACK ) )
- {
- pm->ps->pm_debounce |= PMD_ALTATTACK;
- }
- else
- {
- buttons &= ~BUTTON_ALT_ATTACK;
- }
- }
- else
- {
- pm->ps->pm_debounce &= ~PMD_ALTATTACK;
- }
- }
-
- return buttons;
- }
-
- /*
- ==============
- PM_Weapon_AddInaccuracy
- ==============
- */
- #define ACCURACY_FADERATE 0.2
- #define RECOVER_TIME 800
- #define RECOVER_TIME_SQ 200000.0
-
- static void PM_Weapon_AddInaccuracy( attackType_t attack )
- {
- assert ( attack >= ATTACK_NORMAL && attack < ATTACK_MAX );
-
- // Zoomed sniper weapons don't add innacuracy if ont hte ground
- if( (pm->ps->pm_flags & PMF_ZOOMED) && pml.groundPlane )
- {
- return;
- }
-
- pm->ps->inaccuracy += weaponData[pm->ps->weapon].attack[attack].inaccuracy;
-
- pm->ps->inaccuracyTime = RECOVER_TIME;
-
- if ( pm->ps->inaccuracy > weaponData[pm->ps->weapon].attack[attack].maxInaccuracy )
- pm->ps->inaccuracy = weaponData[pm->ps->weapon].attack[attack].maxInaccuracy;
- }
-
- /*
- ==============
- PM_Weapon_UpdateInaccuracy
- ==============
- */
- static void PM_Weapon_UpdateInaccuracy(void)
- {
- if( pm->ps->inaccuracy <= 0 )
- {
- pm->ps->inaccuracy = 0;
- return;
- }
-
- if ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_FIRING_ALT)
- {
- pm->ps->inaccuracyTime -= (pml.msec * 3 / 4);
- }
- else
- {
- pm->ps->inaccuracyTime -= pml.msec;
- }
-
- if ( pm->ps->inaccuracyTime <= 0 )
- {
- pm->ps->inaccuracy = 0;
- }
- else
- {
- // decrement inaccuracy quadraticly to simulate the player recovering slowly at first, then rapidly
- int diff = RECOVER_TIME - pm->ps->inaccuracyTime;
-
- pm->ps->inaccuracy *= (1 - (diff*diff)/RECOVER_TIME_SQ);
- }
- }
-
- /*
- ==============
- PM_Weapon_AddKickAngles
- ==============
- */
- static void PM_Weapon_AddKickAngles(vec3_t kickAngles)
- {
- // Throw the new kick angles into the integer versions
- pm->ps->kickPitch += (int)(kickAngles[PITCH] * 500.0f);
-
- if ( pm->ps->kickPitch > 180000 )
- pm->ps->kickPitch = 180000;
- }
-
- /*
- ==============
- PM_Weapon_UpdateKickAngles
- ==============
- */
- static void PM_Weapon_UpdateKickAngles(void)
- {
- // bring our kickAngles back down to zero over time
- int i;
- float degreesCorrectedPerMSecond = 0.01f;
- qboolean firing;
- float degreesToCorrect = degreesCorrectedPerMSecond*pml.msec;
-
- vec3_t kickAngles;
-
- // Extract the kick angles from their integer versions
- kickAngles[YAW] = kickAngles[ROLL] = 0;
- kickAngles[PITCH] = (float)pm->ps->kickPitch / 1000.0f;
-
- firing = qfalse;
-
- // Determine if firing or not
- if ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_FIRING_ALT)
- {
- firing = qtrue;
- }
-
- // If not firing then bring it down alot faster.
- if (!firing)
- {
- // return a whole lot faster if not firing
- VectorScale( kickAngles, 1.0f - (0.3f*((float)pml.msec/50.0f)), kickAngles );
-
- for (i = 0; i < 3; i++)
- {
- if (kickAngles[i] >= 0 && kickAngles[i] < 0.05f)
- {
- kickAngles[i] = 0.0f;
- }
- else if (kickAngles[i] <= 0 && kickAngles[i] > -0.05f)
- {
- kickAngles[i] = 0.0f;
- }
- }
- }
- else
- {
- for (i = 0; i < 3; i++)
- {
- if (kickAngles[i] > 0)
- {
- if (kickAngles[i] < degreesToCorrect)
- {
- kickAngles[i] = 0;
- }
- else
- {
- kickAngles[i] -= degreesToCorrect;
- }
- }
- else if (kickAngles[i] < 0)
- {
- if (kickAngles[i] > -degreesToCorrect)
- {
- kickAngles[i] = 0;
- }
- else
- {
- kickAngles[i] += degreesToCorrect;
- }
- }
- }
- }
-
- // Throw the new kick angles into the integer versions
- pm->ps->kickPitch = (int)(kickAngles[PITCH] * 1000.0f);
- }
-
- /*
- ==============
- PM_Goggles
-
- Handles turning goggles on and off
- ==============
- */
- static void PM_Goggles ( void )
- {
- // ignore if not a normal player or dead or a ghost
- if ( pm->ps->pm_type != PM_NORMAL || pm->ps->stats[STAT_HEALTH] <= 0 || (pm->ps->pm_flags & PMF_GHOST) )
- {
- return;
- }
-
- // See if they even have goggles
- if ( pm->ps->stats[STAT_GOGGLES] == GOGGLES_NONE )
- {
- return;
- }
-
- // If the thermal goggles are on and the user has zoomed then turn them off
- if ( pm->ps->stats[STAT_GOGGLES] == GOGGLES_INFRARED )
- {
- // If the player is zoomed then no goggles
- if ( pm->ps->pm_flags & PMF_ZOOMED )
- {
- pm->ps->pm_flags &= ~PMF_GOGGLES_ON;
- return;
- }
- }
-
- // When goggles button isnt down there is nothing to do
- if ( !(pm->cmd.buttons & BUTTON_GOGGLES ) )
- {
- pm->ps->pm_debounce &= ~PMD_GOGGLES;
- return;
- }
-
- // Dont do anything if the goggles button is being held down
- if ( pm->ps->pm_debounce & PMD_GOGGLES )
- {
- return;
- }
-
- // toggle the goggles
- pm->ps->pm_debounce |= PMD_GOGGLES;
- pm->ps->pm_flags ^= PMF_GOGGLES_ON;
-
- // Play some noise
- PM_AddEventWithParm(EV_GOGGLES, (pm->ps->pm_flags&PMF_GOGGLES_ON) ? qtrue : qfalse );
- }
-
- /*
- ==============
- PM_WeaponIdle
-
- Handles the progression of the looping idle animation
- ==============
- */
- static void PM_WeaponIdle ( void )
- {
- pm->ps->weaponAnimTime -= pml.msec;
- if ( pm->ps->weaponAnimTime <= 0 )
- {
- pm->ps->weaponAnimTime = 0;
- if ( pm->ps->weaponTime <= 0 )
- {
- PM_HandleWeaponAction(WACT_IDLE);
- pm->ps->weaponTime = 0;
- }
- }
- }
-
- /*
- ==============
- PM_Weapon
-
- Generates weapon events and modifes the weapon counter
- ==============
- */
- static void PM_Weapon( void )
- {
- int *ammoSource;
- int attackButtons;
- attackData_t *attackData;
- qboolean altFire;
-
- // Get modifed attack buttons.
- attackButtons = PM_GetAttackButtons();
-
- // Gun goes away when using something
- if ( pm->ps->stats[STAT_USEWEAPONDROP] )
- {
- return;
- }
-
- // don't allow attack until all buttons are up
- if ( pm->ps->pm_flags & PMF_RESPAWNED )
- {
- return;
- }
-
- // ignore if not a normal player
- if ( pm->ps->pm_type != PM_NORMAL )
- {
- return;
- }
-
- // check for dead player
- if ( pm->ps->stats[STAT_HEALTH] <= 0 )
- {
- pm->ps->weapon = WP_NONE;
- return;
- }
-
- // Update the weapon inaccuracies and recoil
- PM_Weapon_UpdateInaccuracy();
- PM_Weapon_UpdateKickAngles();
-
- if( pm->ps->weaponTime > 0 )
- {
- pm->ps->weaponTime-=pml.msec;
- }
-
- // See if we've hit a note?
- PM_CheckWeaponNotes();
-
- // Check for weapon change.
- if( pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT )
- {
- // Can't change if weapon is charging.
- // Update the grenade timer
- if ( pm->ps->grenadeTimer > 0 )
- {
- pm->ps->grenadeTimer -= pml.msec;
-
- // Force it to go off if the timer has run out
- if ( pm->ps->grenadeTimer <= 0 )
- {
- pm->ps->grenadeTimer = 1;
- pm->ps->weaponTime = 0;
- attackButtons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
- }
- }
- }
- else if ( pm->ps->weaponstate == WEAPON_SPAWNING )
- {
- if ( pm->cmd.weapon != pm->ps->weapon )
- {
- return;
- }
-
- pm->ps->weaponstate = WEAPON_READY;
- pm->ps->weaponTime = 0;
- }
- else if( pm->ps->weaponTime <= 0 || pm->ps->weaponstate < WEAPON_RELOADING )
- {
- // Dont change weapons if this is a weapon selection
- if ( (pm->cmd.weapon & WP_DELAYED_CHANGE_BIT) && ((pm->cmd.buttons&BUTTON_ATTACK)||(pm->cmd.buttons&BUTTON_ALT_ATTACK)) )
- {
- PM_BeginWeaponChange( pm->cmd.weapon & ~WP_DELAYED_CHANGE_BIT );
- }
- else if ( !(pm->cmd.weapon & WP_DELAYED_CHANGE_BIT) && pm->ps->weapon != pm->cmd.weapon )
- {
- PM_BeginWeaponChange( pm->cmd.weapon );
- }
- }
-
- if ( pm->ps->weaponTime > 0 )
- {
- // Handle the weapons idle animation
- PM_WeaponIdle ( );
-
- return;
- }
-
- // Reload the alt clip immediately
- if( !pm->ps->clip[ATTACK_ALTERNATE][pm->ps->weapon] && pm->ps->ammo[weaponData[pm->ps->weapon].attack[ATTACK_ALTERNATE].ammoIndex] > 0)
- {
- switch(weaponData[pm->ps->weapon].attack[ATTACK_ALTERNATE].fireFromClip)
- {
- case 2:
- // Reload altclip.
- PM_StartRefillClip( ATTACK_ALTERNATE );
- return;
- }
- }
-
- // Select firemode.
- if( attackButtons & BUTTON_FIREMODE )
- {
- pm->ps->firemode[pm->ps->weapon] = BG_FindFireMode( pm->ps->weapon, ATTACK_NORMAL, pm->ps->firemode[pm->ps->weapon] + 1 );
- }
-
- // Decrement burst fire counter if running.
- if(pm->ps->weaponFireBurstCount)
- {
- pm->ps->weaponFireBurstCount--;
- }
-
- // change weapon if time
- if ( pm->ps->weaponstate == WEAPON_DROPPING )
- {
- PM_FinishWeaponChange();
- return;
- }
-
- // Zoom in animation complete... now set zoom parms.
- if( pm->ps->weaponstate == WEAPON_ZOOMIN )
- {
- // The zoomfov may still be remembered from a reload while zooming
- if ( !pm->ps->zoomFov )
- {
- pm->ps->zoomFov = 20;
- }
-
- pm->ps->pm_flags |= PMF_ZOOMED;
- pm->ps->pm_flags |= PMF_ZOOM_LOCKED;
- pm->ps->pm_flags &= ~PMF_ZOOM_REZOOM;
- pm->ps->weaponstate=WEAPON_READY;
- return;
- }
-
- if( pm->ps->weaponstate==WEAPON_CHARGING || pm->ps->weaponstate==WEAPON_CHARGING_ALT )
- {
- switch(pm->ps->weapon)
- {
- case WP_M84_GRENADE:
- case WP_SMOHG92_GRENADE:
- case WP_ANM14_GRENADE:
- case WP_M15_GRENADE:
- if(!(attackButtons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
- {
- if(pm->ps->weaponstate==WEAPON_CHARGING)
- {
- if ( pm->ps->grenadeTimer <= 1 )
- {
- PM_AddEvent(EV_FIRE_WEAPON);
- pm->ps->weaponstate=WEAPON_FIRING;
- pm->ps->weaponTime = 250;
- }
- else
- {
- pm->ps->weaponstate=WEAPON_FIRING;
- PM_HandleWeaponAction(WACT_FIRE);
- PM_StartTorsoAnim( pm->ps, TORSO_ATTACK_GRENADE_END, pm->ps->weaponTime);
- }
- }
- else
- {
- if ( pm->ps->grenadeTimer <= 1 )
- {
- PM_AddEvent(EV_ALT_FIRE);
- pm->ps->weaponstate=WEAPON_FIRING;
- pm->ps->weaponTime = 250;
- }
- else
- {
- pm->ps->weaponstate=WEAPON_FIRING_ALT;
- PM_HandleWeaponAction(WACT_ALTFIRE);
- PM_StartTorsoAnim( pm->ps, TORSO_ATTACK_GRENADE_END, pm->ps->weaponTime);
- }
- }
- return;
- }
- else
- {
- return;
- }
- break;
-
- default:
- break;
- }
- }
-
- // Special end of fire animation for some weapons. Knife currently the only one.
- if( pm->ps->weaponstate == WEAPON_FIRING )
- {
- switch(pm->ps->weapon)
- {
- case WP_KNIFE:
- if(!(pm->cmd.buttons&BUTTON_ATTACK))
- {
- PM_HandleWeaponAction(WACT_FIRE_END);
- pm->ps->weaponTime=0;
- pm->ps->weaponstate=WEAPON_READY;
- return;
- }
- default:
- break;
- }
- }
-
- // ************************************************************************
- // Special reload behavior for warious weapons.
- // ************************************************************************
- if((pm->ps->weaponstate==WEAPON_RELOADING)||(pm->ps->weaponstate==WEAPON_RELOADING_ALT))
- {
- switch(pm->ps->weapon)
- {
- case WP_MM1_GRENADE_LAUNCHER:
- case WP_M590_SHOTGUN:
- // The M590 shotgun has a unique behavior in that when it is reloading,
- // the reload can be interrupted by firing. This cancels the reload of
- // course.
- if(attackButtons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)&&(pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon]>0))
- {
- // Allow normal fire operation and cancel reload. Note: in
- // the interests of gameplay, I allow us to go right to the
- // fire anim, although it doesn't look as smooth as going to
- // reload end and then fire.
- PM_HandleWeaponAction(WACT_RELOAD_END);
- PM_EndRefillClip();
-
- PM_StartTorsoAnim ( pm->ps, weaponData[pm->ps->weapon].animReloadEnd, pm->ps->weaponTime );
-
- return;
- }
- else if( ( pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon] < weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].clipSize) &&
- ( pm->ps->ammo[weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].ammoIndex]>0))
- {
- // Load 1 more shell.
- pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon]++;
- pm->ps->ammo[weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].ammoIndex]--;
- PM_HandleWeaponAction(WACT_RELOAD);
-
- PM_StartTorsoAnim ( pm->ps, weaponData[pm->ps->weapon].animReload, pm->ps->weaponTime );
- return;
- }
- else
- {
- // Weapon fully loaded so play end reload sequence.
- PM_HandleWeaponAction(WACT_RELOAD_END);
- PM_EndRefillClip();
-
- PM_StartTorsoAnim ( pm->ps, weaponData[pm->ps->weapon].animReloadEnd, pm->ps->weaponTime );
-
- return;
- }
- break;
-
- default:
- PM_EndRefillClip();
- return;
- }
- }
- else if(pm->ps->pm_flags & PMF_ZOOM_DEFER_RELOAD )
- {
- PM_StartRefillClip( ATTACK_NORMAL );
- return;
- }
- else if( pm->ps->weapon==WP_KNIFE || (pm->ps->weapon>=WP_RPG7_LAUNCHER && pm->ps->weapon<=WP_M15_GRENADE) )
- {
- if(pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon]<1)
- {
- // Clip is now empty so see if we have enough ammo to reload this weapon?
- if (pm->ps->ammo[weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].ammoIndex] > 0)
- {
- // Yes, so reload it.
- PM_StartRefillClip( ATTACK_NORMAL );
- return;
- }
- else
- {
- if(pm->ps->weapon!=WP_RPG7_LAUNCHER)
- {
- // Clear grenade type from inventory.
- pm->ps->stats[STAT_WEAPONS]&=~(1<<pm->ps->weapon);
-
- // Out of ammo so switch weapons.
- PM_AddEventWithParm(EV_NOAMMO, pm->ps->weapon);
- return;
- }
- }
- }
- }
-
- // Handle zooming in/out for sniper rifle.
- if(pm->ps->weapon==WP_MSG90A1)
- {
- if( (attackButtons&BUTTON_ALT_ATTACK) || (pm->ps->pm_flags & PMF_ZOOM_REZOOM) )
- {
- if( pm->ps->pm_flags & PMF_ZOOMED )
- {
- PM_BeginZoomOut();
- }
- else
- {
- PM_BeginZoomIn();
- }
- return;
- }
- else if( pm->ps->pm_flags & PMF_ZOOMED )
- {
- if(pm->cmd.buttons&BUTTON_ZOOMIN)
- {
- pm->ps->zoomFov = pm->ps->zoomFov >> 1;
- if ( pm->ps->zoomFov < 5)
- {
- pm->ps->zoomFov = 5;
- }
- pm->ps->weaponTime=175;
- return;
- }
- else if(pm->cmd.buttons&BUTTON_ZOOMOUT)
- {
- pm->ps->zoomFov = pm->ps->zoomFov << 1;
- if(pm->ps->zoomFov > 20 )
- {
- pm->ps->zoomFov = 20;
- }
- pm->ps->weaponTime=175;
- return;
- }
- }
- }
-
- // Reload weapon?
- if ( attackButtons & BUTTON_RELOAD )
- {
- if(pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon] < weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].clipSize)
- {
- // No, so see if we have enough ammo to reload this weapon?
- if (pm->ps->ammo[weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].ammoIndex] > 0)
- {
- // Yes, so reload it.
- PM_StartRefillClip( ATTACK_NORMAL );
- return;
- }
- }
- }
-
- // Start weapon when either frozen or not shooting
- if( pm->ps->stats[STAT_FROZEN] || !(attackButtons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
- {
- // Handle the weapons idle animation
- PM_WeaponIdle ( );
-
- pm->ps->weaponstate = WEAPON_READY;
- return;
- }
-
- // Determine whether to use the alternate or normal attack info
- if ( attackButtons & BUTTON_ATTACK )
- {
- altFire = qfalse;
- attackData = &weaponData[pm->ps->weapon].attack[ATTACK_NORMAL];
- }
- else
- {
- altFire = qtrue;
- attackData = &weaponData[pm->ps->weapon].attack[ATTACK_ALTERNATE];
-
- // Cant throw last knife
- if( pm->ps->weapon==WP_KNIFE && pm->ps->ammo[attackData->ammoIndex] < 1 )
- {
- return;
- }
- }
-
- // Ammo taken from pool, clip or altclip?
- switch(attackData->fireFromClip)
- {
- case 0:
- ammoSource = &pm->ps->ammo[attackData->ammoIndex];
- break;
-
- default:
- case 1:
- ammoSource = &pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon];
- break;
-
- case 2:
- ammoSource = &pm->ps->clip[ATTACK_ALTERNATE][pm->ps->weapon];
- break;
- }
-
- // Is there enough ammo to fire?
- if ( (*ammoSource) - attackData->fireAmount < 0 )
- {
- // No, so reload if there is more ammo
- if( pm->ps->ammo[ attackData->ammoIndex ] > 0 )
- {
- // If auto reloading is enabled then reload the gun
- if ( pm->ps->pm_flags & PMF_AUTORELOAD )
- {
- switch ( attackData->fireFromClip)
- {
- case 1:
- // Reload clip.
- PM_StartRefillClip( ATTACK_NORMAL );
- return;
-
- case 2:
- // Reload altclip.
- PM_StartRefillClip( ATTACK_ALTERNATE );
- return;
- }
- }
- }
- // Out of ammo, switch weapons if not an alt-attack
- else if ( !altFire )
- {
- PM_AddEventWithParm (EV_NOAMMO, pm->ps->weapon);
- }
-
- // Handle the weapons idle animation
- PM_WeaponIdle ( );
-
- pm->ps->weaponstate = WEAPON_READY;
-
- return;
- }
-
- // This attack doesnt exist
- if ( !attackData->damage )
- {
- // Handle the weapons idle animation
- PM_WeaponIdle ( );
-
- pm->ps->weaponstate = WEAPON_READY;
-
- return;
- }
-
- // Decrease the ammo
- (*ammoSource) -= attackData->fireAmount;
-
- // Handle charging cases
- switch(pm->ps->weapon)
- {
- case WP_KNIFE:
-
- if ( altFire )
- {
- if( pm->ps->weaponstate != WEAPON_FIRING && pm->ps->weaponstate != WEAPON_FIRING_ALT )
- {
- PM_HandleWeaponAction(WACT_ALTFIRE);
- pm->ps->weaponstate=WEAPON_FIRING_ALT;
- }
- }
- else
- {
- PM_HandleWeaponAction ( WACT_FIRE );
- pm->ps->weaponstate = WEAPON_FIRING;
- }
-
- // Play the torso animation associated with the attack
- PM_StartTorsoAnim ( pm->ps, attackData->animFire, pm->ps->weaponAnimTime );
-
- break;
-
- case WP_M84_GRENADE:
- case WP_SMOHG92_GRENADE:
- case WP_ANM14_GRENADE:
- case WP_M15_GRENADE:
-
- // Start the detonation timer on the grenade going.
- pm->ps->grenadeTimer = attackData->projectileLifetime;
-
- if ( altFire )
- {
- PM_HandleWeaponAction ( WACT_ALTCHARGE );
- pm->ps->weaponstate = WEAPON_CHARGING_ALT;
- }
- else
- {
- PM_HandleWeaponAction ( WACT_CHARGE );
- pm->ps->weaponstate = WEAPON_CHARGING;
- }
-
- PM_StartTorsoAnim( pm->ps, TORSO_ATTACK_GRENADE_START, pm->ps->weaponTime);
-
- break;
-
- default:
-
- if ( altFire )
- {
- PM_HandleWeaponAction(WACT_ALTFIRE);
- pm->ps->weaponstate=WEAPON_FIRING_ALT;
- }
- else
- {
- PM_HandleWeaponAction(WACT_FIRE);
- pm->ps->weaponstate=WEAPON_FIRING;
- }
-
- pm->ps->weaponTime += attackData->fireDelay;
-
- // Play the torso animation associated with the attack
- if ( pm->ps->pm_flags & PMF_ZOOMED )
- {
- PM_StartTorsoAnim ( pm->ps, attackData->animFireZoomed, pm->ps->weaponTime );
- }
- else
- {
- PM_StartTorsoAnim ( pm->ps, attackData->animFire, pm->ps->weaponTime );
- }
-
- break;
- }
- }
-
- /*
- ================
- PM_DropTimers
- ================
- */
- static void PM_DropTimers( void )
- {
- // drop misc timing counter
- if ( pm->ps->pm_time )
- {
- if ( pml.msec >= pm->ps->pm_time )
- {
- pm->ps->pm_flags &= ~PMF_ALL_TIMES;
- pm->ps->pm_time = 0;
- }
- else
- {
- pm->ps->pm_time -= pml.msec;
- }
- }
- }
-
- /*
- ==============
- PM_CheckLean
- ==============
- */
- static void PM_CheckLean( void )
- {
- trace_t trace;
- qboolean canlean;
- float leanTime;
-
- if ( !pm || !pm->ps )
- {
- return;
- }
-
- // No leaning as a spectator or a ghost
- if ( (pm->ps->pm_flags & PMF_GHOST) || pm->ps->pm_type == PM_SPECTATOR )
- {
- pm->ps->leanTime = LEAN_TIME;
- pm->ps->pm_flags &= ~PMF_LEANING;
- return;
- }
-
- leanTime = (float)pm->ps->leanTime - LEAN_TIME;
- canlean = qfalse;
-
- // If their lean button is being pressed and they are on the ground then perform the lean
- if( (pm->cmd.buttons & (BUTTON_LEAN_RIGHT|BUTTON_LEAN_LEFT)) && (pm->ps->groundEntityNum != ENTITYNUM_NONE) )
- {
- vec3_t start, end, right, mins, maxs;
- int leanDir;
-
- if( pm->cmd.buttons & BUTTON_LEAN_RIGHT )
- {
- leanDir = 1;
- }
- else
- {
- leanDir = -1;
- }
-
- // check for collision
- VectorCopy( pm->ps->origin, start );
- start[2] += pm->ps->viewheight;
- AngleVectors( pm->ps->viewangles, NULL, right, NULL );
- VectorSet( mins, -6, -6, -8 );
- VectorSet( maxs, 6, 6, 8 );
-
- // since we're moving the camera over
- // check that move
- VectorMA( start, leanDir * LEAN_OFFSET * 1.25f, right, end );
- pm->trace(&trace, start, mins, maxs, end, pm->ps->clientNum, pm->tracemask );
-
- if ( trace.fraction < 0 || trace.fraction >= 1.0f )
- {
- leanTime += (leanDir * pml.msec);
- if( leanTime > LEAN_TIME )
- {
- leanTime = LEAN_TIME;
- }
- else if( leanTime < -LEAN_TIME )
- {
- leanTime = -LEAN_TIME;
- }
-
- canlean = qtrue;
- }
- else if ( (pm->ps->pm_flags&PMF_LEANING) && trace.fraction < 1.0f )
- {
- int templeanTime = (float)leanDir * (float)LEAN_TIME * trace.fraction;
-
- if ( fabs(templeanTime) < fabs(leanTime) )
- {
- leanTime = templeanTime;
- }
- }
- }
-
- if ( !canlean )
- {
- if( leanTime > 0 )
- {
- leanTime -= pml.msec;
- if( leanTime < 0 )
- {
- leanTime = 0;
- }
- }
- else if ( leanTime < 0 )
- {
- leanTime += pml.msec;
- if( leanTime > 0 )
- {
- leanTime = 0;
- }
- }
- }
-
- // Set a pm flag for leaning for convienience
- if ( leanTime != 0 )
- {
- pm->ps->pm_flags |= PMF_LEANING;
- }
- else
- {
- pm->ps->pm_flags &= ~PMF_LEANING;
- }
-
- // The lean time is kept positive by adding in the base lean time
- pm->ps->leanTime = (int) (leanTime + LEAN_TIME);
- }
-
- /*
- ================
- PM_UpdateViewAngles
-
- This can be used as another entry point when only the viewangles
- are being updated isntead of a full move
- ================
- */
- void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd )
- {
- short temp;
- int i;
- vec3_t kickAngles;
-
- if ( ps->pm_type == PM_INTERMISSION)
- {
- return; // no view changes at all
- }
-
- if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 )
- {
- return; // no view changes at all
- }
-
- // Extract the kcik angles
- kickAngles[PITCH] = ((float)ps->kickPitch / 1000.0f);
- kickAngles[YAW] = kickAngles[ROLL] = 0;
-
- // circularly clamp the angles with deltas
- for (i=0 ; i<3 ; i++)
- {
- temp = cmd->angles[i] + ps->delta_angles[i] - ANGLE2SHORT(kickAngles[i]);
- if ( i == PITCH )
- {
- // don't let the player look up or down more than 90 degrees
- if ( temp > 16000 )
- {
- ps->delta_angles[i] = 16000 - (cmd->angles[i]- ANGLE2SHORT(kickAngles[i]));
- temp = 16000;
- }
- else if ( temp < -16000 )
- {
- ps->delta_angles[i] = -16000 - (cmd->angles[i] - ANGLE2SHORT(kickAngles[i]));
- temp = -16000;
- }
- }
- ps->viewangles[i] = SHORT2ANGLE(temp);
- }
-
- PM_CheckLean ( );
- }
-
- /*
- ================
- PM_AdjustAttackStates
- ================
- */
-
- void PM_AdjustAttackStates( pmove_t *pm )
- {
- int ammoOk;
-
- // Check ammo after usage...
- if(pm->cmd.buttons & BUTTON_ALT_ATTACK)
- {
- ammoOk = pm->ps->ammo[weaponData[pm->ps->weapon].attack[ATTACK_ALTERNATE].ammoIndex] - weaponData[pm->ps->weapon].attack[ATTACK_ALTERNATE].fireAmount;
- }
- else
- {
- ammoOk = pm->ps->clip[ATTACK_NORMAL][pm->ps->weapon ] - weaponData[pm->ps->weapon].attack[ATTACK_NORMAL].fireAmount;
- }
-
- // Set the firing flag.
- if(!(pm->ps->pm_flags & PMF_RESPAWNED) && (pm->ps->pm_type!=PM_INTERMISSION) && (ammoOk>=0))
- {
- if((pm->cmd.buttons & BUTTON_ALT_ATTACK)||(pm->ps->weaponstate==WEAPON_FIRING_ALT))
- {
- pm->ps->eFlags |= EF_ALT_FIRING;
- }
- else if((pm->cmd.buttons & BUTTON_ATTACK)||(pm->ps->weaponstate==WEAPON_FIRING))
- {
- pm->ps->eFlags &= ~EF_ALT_FIRING;
- }
-
- // This flag should always get set, even when alt-firing
- pm->ps->eFlags |= EF_FIRING;
- }
- else
- {
- // Clear 'em out
- pm->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
- }
- }
-
- /*
- ================
- PmoveSingle
- ================
- */
- void trap_SnapVector( float *v );
-
- void PmoveSingle (pmove_t *pmove) {
- pm = pmove;
-
- // this counter lets us debug movement problems with a journal
- // by setting a conditional breakpoint fot the previous frame
- c_pmove++;
-
- // clear results
- pm->numtouch = 0;
- pm->watertype = 0;
- pm->waterlevel = 0;
-
- if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
- pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies
- }
-
- // make sure walking button is clear if they are running, to avoid
- // proxy no-footsteps cheats
- if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
- pm->cmd.buttons &= ~BUTTON_WALKING;
- }
-
- // When the lean modifier button is held the strafe left and right keys
- // will act as lean left and right
- if ( pm->cmd.buttons & BUTTON_LEAN )
- {
- pm->cmd.buttons &= ~(BUTTON_LEAN_LEFT|BUTTON_LEAN_RIGHT);
-
- // Strafe left = lean left
- if ( pm->cmd.rightmove < 0 )
- {
- pm->cmd.buttons |= BUTTON_LEAN_LEFT;
- }
- // Strafe right = lean right
- else if ( pm->cmd.rightmove > 0 )
- {
- pm->cmd.buttons |= BUTTON_LEAN_RIGHT;
- }
-
- // NO strafing with lean button down
- pm->cmd.rightmove = 0;
- }
-
- // Cant move when leaning
- if ( (pm->cmd.buttons & (BUTTON_LEAN_LEFT|BUTTON_LEAN_RIGHT)))
- {
- // pm->cmd.rightmove = 0;
- pm->cmd.forwardmove = 0;
-
- // Cant jump when leaning
- if ( pm->cmd.upmove > 0 )
- {
- pm->cmd.upmove = 0;
- }
- }
-
- // Cant run when zoomed, leaning, or using something that takes time
- if ( (pm->ps->pm_flags&PMF_ZOOMED) ||
- (pm->ps->weaponstate == WEAPON_ZOOMIN) ||
- (pm->cmd.buttons & (BUTTON_LEAN_LEFT|BUTTON_LEAN_RIGHT)) ||
- (pm->ps->stats[STAT_USEWEAPONDROP]) )
- {
- if ( pm->cmd.forwardmove > 64 )
- {
- pm->cmd.forwardmove = 64;
- }
- else if ( pm->cmd.forwardmove < -64 )
- {
- pm->cmd.forwardmove = -64;
- }
-
-
- if ( pm->cmd.rightmove > 64 )
- {
- pm->cmd.rightmove = 64;
- }
- else if ( pm->cmd.rightmove < -64 )
- {
- pm->cmd.rightmove = -64;
- }
-
- pm->cmd.buttons |= BUTTON_WALKING;
- }
-
- // set the talk balloon flag
- if ( pm->cmd.buttons & BUTTON_TALK )
- {
- pm->ps->eFlags |= EF_TALK;
- }
- else
- {
- pm->ps->eFlags &= ~EF_TALK;
- }
-
- // In certain situations, we may want to control which attack buttons are pressed and what kind of functionality
- // is attached to them
- PM_AdjustAttackStates( pm );
-
- // clear the respawned flag if attack and use are cleared
- if ( pm->ps->stats[STAT_HEALTH] > 0 && !( pm->cmd.buttons & BUTTON_ATTACK) )
- {
- pm->ps->pm_flags &= ~PMF_RESPAWNED;
- }
-
- // if talk button is down, dissallow all other input
- // this is to prevent any possible intercept proxy from
- // adding fake talk balloons
- if ( pmove->cmd.buttons & BUTTON_TALK )
- {
- // keep the talk button set tho for when the cmd.serverTime > 66 msec
- // and the same cmd is used multiple times in Pmove
- pmove->cmd.buttons = BUTTON_TALK;
- pmove->cmd.forwardmove = 0;
- pmove->cmd.rightmove = 0;
- pmove->cmd.upmove = 0;
- }
-
- // clear all pmove local vars
- memset (&pml, 0, sizeof(pml));
-
- // determine the time
- pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
- if ( pml.msec < 1 ) {
- pml.msec = 1;
- } else if ( pml.msec > 200 ) {
- pml.msec = 200;
- }
- pm->ps->commandTime = pmove->cmd.serverTime;
-
- // Frozen?
- if ( pm->ps->stats[STAT_FROZEN] )
- {
- pm->ps->stats[STAT_FROZEN] -= pml.msec;
- if ( pm->ps->stats[STAT_FROZEN] < 0 )
- {
- pm->ps->stats[STAT_FROZEN] = 0;
- }
- else
- {
- // pm->cmd.buttons = pm->cmd.buttons & (BUTTON_RELOAD|BUTTON_ATTACK|BUTTON_);
- pm->cmd.forwardmove = 0;
- pm->cmd.rightmove = 0;
- pm->cmd.upmove = 0;
- }
- }
-
- // save old org in case we get stuck
- VectorCopy (pm->ps->origin, pml.previous_origin);
-
- // save old velocity for crashlanding
- VectorCopy (pm->ps->velocity, pml.previous_velocity);
-
- pml.frametime = pml.msec * 0.001;
-
- // update the viewangles
- PM_UpdateViewAngles( pm->ps, &pm->cmd );
-
- AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);
-
- if ( pm->cmd.upmove < 10 )
- {
- // not holding jump
- pm->ps->pm_debounce &= ~PMD_JUMP;
- }
-
- // decide if backpedaling animations should be used
- if ( pm->cmd.forwardmove < 0 ) {
- pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
- } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
- pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
- }
-
- if ( pm->ps->pm_type >= PM_DEAD ) {
- pm->cmd.forwardmove = 0;
- pm->cmd.rightmove = 0;
- pm->cmd.upmove = 0;
- }
-
- if ( pm->ps->pm_type == PM_SPECTATOR )
- {
- pm->mins[0] = -15;
- pm->mins[1] = -15;
- pm->maxs[0] = 15;
- pm->maxs[1] = 15;
- pm->mins[2] = MINS_Z;
- pm->maxs[2] = DEFAULT_PLAYER_Z_MAX;
- pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
-
- // PM_FlyMove ();
- PM_NoclipMove ( );
- PM_DropTimers ();
- return;
- }
-
- if ( pm->ps->pm_type == PM_NOCLIP ) {
- PM_NoclipMove ();
- PM_DropTimers ();
- return;
- }
-
- if (pm->ps->pm_type == PM_FREEZE) {
- return; // no movement at all
- }
-
- if ( pm->ps->pm_type == PM_INTERMISSION )
- {
- return; // no movement at all
- }
-
- // set mins, maxs, and viewheight
- PM_CheckDuck ();
-
- // set groundentity
- PM_GroundTrace();
-
- // set watertype, and waterlevel
- PM_SetWaterLevel();
- pml.previous_waterlevel = pmove->waterlevel;
-
- PM_CheckCrouchJump ( );
-
- if ( pm->ps->pm_type == PM_DEAD ) {
- PM_DeadMove ();
- }
-
- PM_DropTimers();
-
- if ( pm->ps->pm_flags & PMF_LADDER )
- {
- PM_LadderMove ( );
- }
- else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
- {
- PM_WaterJumpMove();
- }
- else if ( pm->waterlevel > 1 )
- {
- // swimming
- PM_WaterMove();
- }
- else if ( pml.walking )
- {
- // walking on ground
- PM_WalkMove();
- }
- else
- {
- // airborne
- PM_AirMove();
- }
-
- // set groundentity, watertype, and waterlevel
- if(!(VectorCompare(pm->ps->origin,pml.previous_origin)))
- {
- // If we didn't move at all, then why bother doing this again -MW.
- PM_GroundTrace();
- }
-
- PM_SetWaterLevel();
-
- // turn goggles on/off
- PM_Goggles ( );
-
- // weapons
- PM_Weapon();
-
- // Use
- PM_Use ( );
-
- // torso animation
- PM_TorsoAnimation( pm->ps );
-
- // footstep events / legs animations
- PM_Footsteps();
-
- // entering / leaving water splashes
- PM_WaterEvents();
-
- // snap some parts of playerstate to save network bandwidth
- trap_SnapVector( pm->ps->velocity );
- }
-
- /*
- ================
- PM_UpdatePVSOrigin
-
- The pvs of the client is calculated using its own origin and this function
- ensures that the origin is set correctly. The main reason for having a PVS
- origin is that when leaning your can poke around corners which will in turn
- change what you can see.
- ================
- */
- void PM_UpdatePVSOrigin ( pmove_t *pmove )
- {
- pm = pmove;
-
- // Set a pm flag for leaning and calculate the view origin for the lean
- if ( pm->ps->leanTime - LEAN_TIME != 0 )
- {
- vec3_t right;
- float leanOffset;
-
- leanOffset = (float)(pm->ps->leanTime - LEAN_TIME) / LEAN_TIME * LEAN_OFFSET;
-
- AngleVectors( pm->ps->viewangles, NULL, right, NULL );
- VectorMA( pm->ps->origin, leanOffset, right, pm->ps->pvsOrigin );
- }
- else
- {
- VectorCopy ( pm->ps->origin, pm->ps->pvsOrigin );
- }
- }
-
- /*
- ================
- Pmove
-
- Can be called by either the server or the client
- ================
- */
- void Pmove (pmove_t *pmove) {
- int finalTime;
-
- finalTime = pmove->cmd.serverTime;
-
- if ( finalTime < pmove->ps->commandTime ) {
- return; // should not happen
- }
-
- if ( finalTime > pmove->ps->commandTime + 1000 ) {
- pmove->ps->commandTime = finalTime - 1000;
- }
-
- pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);
-
- // chop the move up if it is too long, to prevent framerate
- // dependent behavior
- while ( pmove->ps->commandTime != finalTime ) {
- int msec;
-
- msec = finalTime - pmove->ps->commandTime;
-
- if ( pmove->pmove_fixed ) {
- if ( msec > pmove->pmove_msec ) {
- msec = pmove->pmove_msec;
- }
- }
- else {
- if ( msec > 66 ) {
- msec = 66;
- }
- }
- pmove->cmd.serverTime = pmove->ps->commandTime + msec;
- PmoveSingle( pmove );
-
- PM_UpdatePVSOrigin ( pmove );
-
- if ( pmove->ps->pm_debounce & PMD_JUMP )
- {
- pmove->cmd.upmove = 20;
- }
- }
- }
-
- /*
- ================
- BG_AddLadder
-
- Adds a ladder to the ladder list
- ================
- */
- void BG_AddLadder ( vec3_t absmin, vec3_t absmax, vec3_t fwd )
- {
- pm_ladders[pm_laddercount].origin[0] = (absmax[0] + absmin[0]) / 2;
- pm_ladders[pm_laddercount].origin[1] = (absmax[1] + absmin[1]) / 2;
- pm_ladders[pm_laddercount].origin[2] = (absmax[2] + absmin[2]) / 2;
- VectorCopy ( fwd, pm_ladders[pm_laddercount].fwd );
- pm_laddercount++;
- }
-
- /*
- ================
- BG_FindLadder
-
- Searches through the ladder list and finds the closes to the given origin
- ================
- */
- int BG_FindLadder ( vec3_t pos )
- {
- int ladder;
- int result;
- float dist;
-
- dist = 999999.0f;
- result = -1;
-
- for ( ladder = 0; ladder < pm_laddercount; ladder ++ )
- {
- float dist2 = DistanceSquared( pos, pm_ladders[ladder].origin );
-
- if ( dist2 < dist )
- {
- vec3_t diff;
- VectorSubtract ( pm_ladders[ladder].origin, pos, diff );
- diff[2] = 0;
-
- if ( VectorLengthSquared ( diff ) < 500 * 500 )
- {
- dist = dist2;
- result = ladder;
- }
- }
- }
-
- return result;
- }
-
-